这三个东西若不是经常使用,我想也没人记得住它们各自的作用和相互的区别。我以前总是在用到它们的时候再回头温习一遍,每次温习的经历都不是像见到老朋友般如沐春风,而是难以名状的烦躁和郁闷。今天又遇到需要使用它们的时候,为了以后不再痛苦,我便将关于它们的要点记录下来。
编程编的久了,总会遇到多线程的情况,有些时候我们要几个线程合作完成某个功能,这时候可以定义一个全局对象,各个线程根据这个对象的状态来进行各自的工作。ManualResetEvent、AutoResetEvent和Mutex就相当于这样的一种对象类型,而它们的作用类似,也比较单一,就是阻塞相关线程和取消阻塞相关线程。
以下这段话和代码摘自ManualResetEvent和AutoResetEvent的区别:
ManualResetEvent可以阻塞一个或多个线程,直到收到一个信号告诉ManualResetEvent不要再阻塞当前的线程。
可以想象ManualResetEvent这个对象内部有一个Boolean类型的属性IsRelease来控制是否要阻塞当前线程。这个属性我们在初始化的时候可以设置它,如ManualResetEvent event=new ManualResetEvent(false);这就表明默认的属性是要阻塞当前线程。
代码举例:
1 ManualResetEvent _manualResetEvent = new ManualResetEvent(false);
2
3 private void BT_Temp_Click(object sender, RoutedEventArgs e)
4 {
5 Thread t1 = new Thread(this.Thread1Foo);
6 t1.Start(); //启动线程1
7 Thread t2 = new Thread(this.Thread2Foo);
8 t2.Start(); //启动线程2
9 Thread.Sleep(3000); //睡眠当前主线程,即调用BT_Temp_Click的线程
10 _manualResetEvent.Set(); //想象成将IsRelease设为True
11 }
12
13 void Thread1Foo()
14 {
15 _manualResetEvent.WaitOne();
16 //阻塞线程1,直到主线程发信号给线程1,告知_menuResetEvent你的IsRelease属性已经为true,
17 //这时不再阻塞线程1,程序继续往下跑
18 MessageBox.Show("t1 end");
19 }
20
21 void Thread2Foo()
22 {
23 _manualResetEvent.WaitOne();
24 //阻塞线程2,直到主线程发信号给线程1,告知_menuResetEvent你的IsRelease属性已经为true,
25 //这时不再阻塞线程2,程序继续往下跑
26 MessageBox.Show("t2 end");
27 }
注意这里ManualResetEvent和AutoResetEvent的一个重要区别:manual的话肯定会给线程1和线程2都发送一个信号,而auto只会随机给其中一个发送信号。这里引出了另一个问题——为什么一个叫manual而一个叫auto呢?我想这是很多人的疑问,现在我们就来解释这个问题。
刚才_manualResetEvent .Set();的这句话我想大家都明白了,可以看做将IsRelease的属性设置为true.线程1中 _manualResetEvent.WaitOne();接收到信号后不再阻塞线程1。在此之后的整个过程中IsRelease的值都是true.如果想将IsRelease的值回复成false,就必须再调用_manualResetEvent.Reset()的方法。如果是_autoResetEvent.set(),那么_autoResetEvent.WaitOne()后会自动将IsRelease的值自动设置为false.
我们再来说说Mutex对象,这个对象比较“专制”,同时段内只能准许一个线程工作。貌似通过给它取名的方式,还能控制多个进程的工作(这在我以前做的一个项目中验证过),可能相同名字的Mutex在操作系统中只能存在一个。
这三个类继承自同一个基类WaitHandle,这个基类有个方法令人困惑WaitOne(int millisecondsTimeout, bool exitContext),解释为Blocks the current thread until the current System.Threading.WaitHandle receives a signal, using a 32-bit signed integer to measure the time interval and specifying whether to exit the synchronization domain before the wait。难以理解的是最后一句:在等待前退出同步域。经过我艰苦不懈地研究发现了隐藏其中的奥秘(我只想说:干!)。社区论坛也有人跟我一样苦恼于此:autoEvent.WaitOne(1000,true)第二个参数啥作用啊,有高手知道吗?下面摘录高手回答。
假设有A线程和B线程,下面是相关代码:
1 using System;
2 using System.Threading;
3
4 [System.Runtime.Remoting.Contexts.Synchronization(true)] // 1
5 class My : ContextBoundObject
6 {
7 static void Main(string[] args)
8 {
9 My my = new My();
10 ThreadPool.QueueUserWorkItem(my.FuncA);
11 Thread.Sleep(50);
12 ThreadPool.QueueUserWorkItem(my.FuncA);
13 Thread.Sleep(50);
14 ThreadPool.QueueUserWorkItem(my.FuncB);
15 Thread.Sleep(50);
16 ThreadPool.QueueUserWorkItem(my.FuncA);
17 Console.ReadLine();
18 }
19 AutoResetEvent myEvent = new AutoResetEvent(false);
20 public void FuncA(object state)
21 {
22 Console.WriteLine("Start A");
23 System.Threading.Thread.Sleep(2000);
24 Console.WriteLine("End A");
25 }
26 public void FuncB(object state)
27 {
28 Console.WriteLine("Start B");
29 myEvent.WaitOne(10 * 1000, true); // 2
30 Console.WriteLine("End B");
31 }
32 }
在大部分情况下那个参数是没有用的。
只有在使用ContextBoundObject来进行同步的时候,那个参数才有用。
1、Synchronization可以用来同步一个类(基于ContextBoundObject)。
比如上面的例子,同一时间只能运行一个方法。把标志行1注释掉,则可观察到乱序执行。
2、WaitOne(...,true)使得方法B可以暂时脱离ContextBound同步保护,让A有机会在B等待的时候得到执行。比如以下的例子的结果为:
...
Start B
Start A
End A
End B
把标志行2的true改成false,那么A就要等到B执行后才能执行。
转载请注明本文出处:http://www.cnblogs.com/newton/archive/2012/11/29/2793928.html