(精华)2020年12月23日 .NET Core 多线程底层详解(互斥锁)
互斥锁:Monitor 和 mutex
定义:private static readonly object Lock = new object();
使用:Monitor.Enter(Lock); //todo Monitor.Exit(Lock);
作用:将会锁住代码块的内容,并阻止其他线程进入该代码块,直到该代码块运行完成,释放该锁。
注意:定义的锁对象应该是 私有的,静态的,只读的,引用类型的对象,这样可以防止外部改变锁对象
Monitor有TryEnter的功能,可以防止出现死锁的问题,lock没有
定义:private static readonly Mutex mutex = new Mutex();
使用:mutex.WaitOne(); //todo mutex.ReleaseMutex();
作用:将会锁住代码块的内容,并阻止其他线程进入该代码块,直到该代码块运行完成,释放该锁。
注意:定义的锁对象应该是 私有的,静态的,只读的,引用类型的对象,这样可以防止外部改变锁对象
Mutex本身是可以系统级别的,所以是可以跨越进程的
Monitor 测试实现:
using System;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Sample5_2_monitor_lock
{
class Program
{
private static int _TaskNum = 3;
private static Task[] _Tasks;
private static StringBuilder _StrBlder;
private const int RUN_LOOP = 50;
private static void Work1(int TaskID)
{
int i = 0;
string log = "";
bool lockToken = false;
while (i < RUN_LOOP)
{
log = String.Format("Time: {0} Task : #{1} Value: {2} =====\n",
DateTime.Now.TimeOfDay, TaskID, i);
i++;
try
{
lockToken = false;
Monitor.Enter(_StrBlder, ref lockToken);
_StrBlder.Append(log);
}
finally
{
if (lockToken)
Monitor.Exit(_StrBlder);
}
}
}
private static void Work2(int TaskID)
{
int i = 0;
string log = "";
bool lockToken = false;
while (i < RUN_LOOP)
{
log = String.Format("Time: {0} Task : #{1} Value: {2} *****\n",
DateTime.Now.TimeOfDay, TaskID, i);
i++;
try
{
lockToken = false;
Monitor.Enter(_StrBlder, ref lockToken);
_StrBlder.Append(log);
}
finally
{
if (lockToken)
Monitor.Exit(_StrBlder);
}
}
}
private static void Work3(int TaskID)
{
int i = 0;
string log = "";
bool lockToken = false;
while (i < RUN_LOOP)
{
log = String.Format("Time: {0} Task : #{1} Value: {2} ~~~~~\n",
DateTime.Now.TimeOfDay, TaskID, i);
i++;
try
{
lockToken = false;
Monitor.Enter(_StrBlder, ref lockToken);
_StrBlder.Append(log);
}
finally
{
if (lockToken)
Monitor.Exit(_StrBlder);
}
}
}
static void Main(string[] args)
{
_Tasks = new Task[_TaskNum];
_StrBlder = new StringBuilder();
_Tasks[0] = Task.Factory.StartNew((num) =>
{
var taskid = (int)num;
Work1(taskid);
}, 0);
_Tasks[1] = Task.Factory.StartNew((num) =>
{
var taskid = (int)num;
Work2(taskid);
}, 1);
_Tasks[2] = Task.Factory.StartNew((num) =>
{
var taskid = (int)num;
Work3(taskid);
}, 2);
var finalTask = Task.Factory.ContinueWhenAll(_Tasks, (tasks) =>
{
Task.WaitAll(_Tasks);
Console.WriteLine("==========================================================");
Console.WriteLine("All Phase is completed");
Console.WriteLine("==========================================================");
Console.WriteLine(_StrBlder);
});
try
{
finalTask.Wait();
}
catch (AggregateException aex)
{
Console.WriteLine("Task failed And Canceled" + aex.ToString());
}
finally
{
}
Console.ReadLine();
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
锁超时的使用:
其中主要使用的是 Monitor.TryEnter(),函数,其中多了一个设置超时时间的参数。
代码中让每个锁的超时Timer为2秒,在Work1中挺顿5秒,这样造成了Work2和Work3的超时。
using System;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Sample5_3_monitor_lock_timeout
{
class Program
{
private static int _TaskNum = 3;
private static Task[] _Tasks;
private static StringBuilder _StrBlder;
private const int RUN_LOOP = 50;
private static void Work1(int TaskID)
{
int i = 0;
string log = "";
bool lockToken = false;
while (i < RUN_LOOP)
{
log = String.Format("Time: {0} Task : #{1} Value: {2} =====\n",
DateTime.Now.TimeOfDay, TaskID, i);
i++;
try
{
lockToken = false;
Monitor.TryEnter(_StrBlder, 2000, ref lockToken);
if (!lockToken)
{
Console.WriteLine("Work1 TIMEOUT!! Will throw Exception");
throw new TimeoutException("Work1 TIMEOUT!!");
}
System.Threading.Thread.Sleep(5000);
_StrBlder.Append(log);
}
finally
{
if (lockToken)
Monitor.Exit(_StrBlder);
}
}
}
private static void Work2(int TaskID)
{
int i = 0;
string log = "";
bool lockToken = false;
while (i < RUN_LOOP)
{
log = String.Format("Time: {0} Task : #{1} Value: {2} *****\n",
DateTime.Now.TimeOfDay, TaskID, i);
i++;
try
{
lockToken = false;
Monitor.TryEnter(_StrBlder, 2000, ref lockToken);
if (!lockToken)
{
Console.WriteLine("Work2 TIMEOUT!! Will throw Exception");
throw new TimeoutException("Work2 TIMEOUT!!");
}
_StrBlder.Append(log);
}
finally
{
if (lockToken)
Monitor.Exit(_StrBlder);
}
}
}
private static void Work3(int TaskID)
{
int i = 0;
string log = "";
bool lockToken = false;
while (i < RUN_LOOP)
{
log = String.Format("Time: {0} Task : #{1} Value: {2} ~~~~~\n",
DateTime.Now.TimeOfDay, TaskID, i);
i++;
try
{
lockToken = false;
Monitor.TryEnter(_StrBlder, 2000, ref lockToken);
if (!lockToken)
{
Console.WriteLine("Work3 TIMEOUT!! Will throw Exception");
throw new TimeoutException("Work3 TIMEOUT!!");
}
_StrBlder.Append(log);
}
finally
{
if (lockToken)
Monitor.Exit(_StrBlder);
}
}
}
static void Main(string[] args)
{
_Tasks = new Task[_TaskNum];
_StrBlder = new StringBuilder();
_Tasks[0] = Task.Factory.StartNew((num) =>
{
var taskid = (int)num;
Work1(taskid);
}, 0);
_Tasks[1] = Task.Factory.StartNew((num) =>
{
var taskid = (int)num;
Work2(taskid);
}, 1);
_Tasks[2] = Task.Factory.StartNew((num) =>
{
var taskid = (int)num;
Work3(taskid);
}, 2);
var finalTask = Task.Factory.ContinueWhenAll(_Tasks, (tasks) =>
{
Task.WaitAll(_Tasks);
Console.WriteLine("==========================================================");
Console.WriteLine("All Phase is completed");
Console.WriteLine("==========================================================");
Console.WriteLine(_StrBlder);
});
try
{
finalTask.Wait();
}
catch (AggregateException aex)
{
Console.WriteLine("Task failed And Canceled" + aex.ToString());
}
finally
{
}
Console.ReadLine();
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
mutex 测试实现 :
1、initiallyOwned表示创建mutex的线程是否拥有该互斥体。true表示创建线程拥有互斥锁,只有在创建线程中调用ReleaseMutex释放后,其他等待线程才能参与抢夺互斥体的活动。false表示互斥锁体于与空闲状态,其他等待互斥锁的线程立即参与到抢夺互斥锁的活动中去。
2、在上面程序中如果创建mutex时使用true参数,故在启动其他线程后必须执行mutex.ReleaseMutex(),如果不释放mutex,则其他线程将一直等待下去。使用ture,相当于一创建就使用waitone()
3、mutex.WaitOne()与mutex.ReleaseMutex()要像 { } 一样配对使用,否则将出现 “由于出现被放弃的 mutex,等待过程结束” 的异常
4、mutex与monitor相比,没有暂时释放的功能;因此mutex一经释放,原释放资源的线程也将重新参与新一轮对mutex的争夺过程。
using (var mutex = new Mutex(false, "name"))
{
try
{
mutex.WaitOne();
//do something
}
catch(Exception ex)
{
throw ex;
}
finally
{
mutex.ReleaseMutex();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
简单例子:实现递归
//递归里的锁变量不相干
public static class RecursiveSample
{
private static readonly Mutex Lock = new Mutex();
private static int _counterA = 0;
private static int _counterB = 0;
public static void IncrementCounterA()
{
// 获取锁
Lock.WaitOne();
try
{
_counterA++;
_counterB++;
}
finally
{
// 释放锁
Lock.ReleaseMutex();
}
}
public static void IncrementCounterB()
{
// 获取锁
Lock.WaitOne();
try
{
_counterA++;
_counterB++;
}
finally
{
// 释放锁
Lock.ReleaseMutex();
}
}
public static void IncrementCounter()
{
// 获取锁
Lock.WaitOne();
try
{
IncrementCounterA();
IncrementCounterB();
}
finally
{
// 释放锁
Lock.ReleaseMutex();
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
文章来源: codeboy.blog.csdn.net,作者:愚公搬代码,版权归原作者所有,如需转载,请联系作者。
原文链接:codeboy.blog.csdn.net/article/details/108394755
- 点赞
- 收藏
- 关注作者
评论(0)