[深夜原创]C# Monitor通知机制的一个有趣的实例
主要使用Monitor类,对一个对象进行等待和通知的操作
static void Main(string[] args) { //Task启动等待对象 object taskStartSyncRoot = new object(); //多线程等待对象 object syncRoot = new object(); //多线程全部进入等待状态对象 object parallelStartSyncRoot = new object(); lock (taskStartSyncRoot) { Task.Factory.StartNew(() => { lock (parallelStartSyncRoot) { lock (taskStartSyncRoot) { //通知Task已经启动完毕 Monitor.Pulse(taskStartSyncRoot); } Console.WriteLine("Wait all parallel task started."); //等待所有并行任务已经进行等待状态,并释放parallelStartSyncRoot锁 Monitor.Wait(parallelStartSyncRoot); lock (syncRoot) { //通知所有并行任务syncRoot已经释放 Monitor.PulseAll(syncRoot); Console.WriteLine("All syncRoot waiter notified."); } } }); //等待Task启动完毕,并释放taskStartSyncRoot对象锁 Monitor.Wait(taskStartSyncRoot); } //并行等待任务计数器,当该计数器为10时,则通知所有并行任务已经进入等待状态 int counter = 0; Parallel.For(0, 10, (i) => { lock (syncRoot) { //使用原子锁增加counter,当计数器为10时代表所有并行任务已经启动 if (Interlocked.Increment(ref counter) == 10) { lock (parallelStartSyncRoot) { //通知并行任务已经全部启动,并释放parallelStartSyncRoot锁 Monitor.Pulse(parallelStartSyncRoot); } } Console.WriteLine(string.Format("[{0}] Waiting For syncRoot notify.", i)); //等待syncRoot通知,并释放syncRoot锁 Monitor.Wait(syncRoot); Console.WriteLine(string.Format("[{0}] syncRoot notify received.", i)); } }); Console.Read(); }
该程序是个多线程的应用,程序的逻辑将会严格按照以下顺序执行
(1)[主线程] 启动Task线程,等待taskStartSyncRoot,暂时释放syncRoot锁
(2)[TASK线程] 锁住parallelStartSyncRoot 和 taskStartSyncRoot(这里会等待主线程进入Wait状态),通知taskStartSyncRoot已经启动
(3)[TASK线程] 等待parallelStartSyncRoot,暂时释放parallelStartSyncRoot锁
(4)[Parallel子线程] Parallel并行任务启动,并且等待syncRoot,暂时释放syncRoot锁
(5)[最后一个Parallel子线程] 通知parallelStartSyncRoot已经全部启动
(6)[TASK线程] 等待锁syncRoot
(7)[最后一个Parallel子线程] 等待syncRoot,这时会暂时释放syncRoot锁
(8)[TASK线程] 获得syncRoot锁,并且通知(Monitor.PulseAll)所有syncRoot等待者(所有并行任务)。
程序的执行结果:
总结:要理解该多线程程序,必须要理解这三个锁对象的操作,以及通知机制的应用,从而可以让Task和多个并行任务有序的进行执行,大家在这里需要掌握几个知识点:
(1)在Monitor.Wait对象时必须获得目标对象的锁,以确保进入临界状态
(2)Monitor.Wait方法调用时,会将目标对象锁释放,在Wait完成时会重新获得该锁