本节主要内容
** 同步构造,基元构造,用户模式构造,易变构造 **
** volatile CLR 保证以下类型的读写操作是原子操作 **
** System.Threading.Monitor **
Boolean,Char,(S)Byte,(U)Int16,(U)Int32,(U)IntPtr,Single,and reference types
注意,这里64位,比如int64,Double不是原子操作。
虽然CLR保证上面类型的操作是原子操作,但是CLR并没有保证读写操作来自编译器,或者CPU优化。
C#编译器,JIT编译器,CPU都可能优化你的代码。
多个线程,访问共享资源,需要线程同步。
解决方案:
(1)首先考虑,调整设计,避免使用共享资源
(2)其次,考虑原子操作,好处是,没有等待,没有死锁
(3)当代码比较复杂,前面2个都不能解决时,我们需要用到同步构造
一种同步构造,是让等待的线程,进入阻塞状态,使用最少的CPU,需要上下文切换,这种就是内核模式同步构造,适合等待时间较长的操作。
.NET多线程,并发同步,同步构造,基元构造,用户模式构造,易变构造,volatile
另一种同步构造,就是等待,这种就需要使用CPU,没有上下文切换,适合非常短时的等待,性能较好,这也称为用户模式同步构造。
第三种,就是混合模式同步构造了,首先使用用户模式同步构造,如果操作时间长了,就使用内核模式同步构造。
优化器,通常只保证单线程,实现你的代码意图(注意,并不一定按你代码写的那样去实现你的意图)
所以,如果是多线程,那么优化后的代码,则可能有潜在的问题。
class Program
{
private static bool stopFlag = false;
static void Main(string[] args)
{
Thread thread = new Thread(Worker);
thread.Start();
Console.WriteLine("主线程等1秒");
Thread.Sleep(1 * 1000);
Console.WriteLine("主线程通知工作线程结束");
stopFlag = true;
thread.Join();
}
private static void Worker(Object o)
{
int x = 0;
while (!stopFlag)
{
x++;
}
Console.WriteLine("工作线程结束:x = {0}", x);
}
}
上面这段代码,在 Debug 模式,应该没有问题,在 Release模式,则可能有问题。
问题可能是,Worker 的 while 会死循环。
编译器会优化stopFlag,编译器发现在线程 Worker 方法中,stopFlag没有被改变。
所以编译器,会首先判断stopFlag,如果为flase,那 x = 0
如果为 true,则无限递增 x,这样只会判断1次stopFlag
主线程的stopFlag = true 则可能是在 worker 优化之后,才编译的
具体,这些都是编译器的事情。
我们只需要知道存在,编译器优化,优化可能带来问题。
解决死循环这个问题:加 volatile
private volatile static bool stopFlag = false;
(2)System.Threading.Monitor
(1)混合模式同步构造
(2)互斥,自旋,线程所有权,迭代
(1)同步多个字段,属性,项目
(2)当前线程已经获取锁的情况,再调用 Monitor.Enter 无需再等待获取锁
.NET多线程(六)并发同步,同步构造 Monitor
(1)System.Threading.Monitor 的问题
lock ,自动 try/catch Monitor ,在 try 中出错了怎么办,try 中数据的状态是正确的? 后续线程会使用这些数据?
使用示例
private static readonly object obj = new object();
static void Main(string[] args)
{
int tCount = 1000;
int result = 0;
List<Task> taskList = new List<Task>();
for (int i = 0; i < tCount; i++)
{
Task task = Task.Factory.StartNew(() =>
{
for (int j = 0; j < 1000; j++)
{
bool taken = false;
try // try/finally 保证锁释放
{
Monitor.Enter(obj, ref taken);
result = result + 1;
}
finally // Enter异常 taken = False
{
if (taken) { Monitor.Exit(obj); }
}
}
});
taskList.Add(task);
}
Task.WaitAll(taskList.ToArray());
Console.WriteLine(result);
Console.ReadLine();
}
lock 是 Monitor 上面代码的,语法糖写法
lock 不支持获取锁超时
lock (obj)
{
result = result + 1;
}
使用 Monitor.TryEnter 判断获取锁是否超时
使用 Monitor 传递信号
Monitor.Wait 表示当前线程放弃锁,进入等待状态
Monitor.Pulse 告诉1个wait线程,锁状态更改
Monitor.PulseAll 告诉所有wait线程,锁状态更改
网友评论