学习 Monitor使用
准备类:
OperationItem
public class OperationItem { public int Num { get ; set ; } public bool HasInit { get ; set ; } public void Add( int number) { Num += number; Debug.Print( " {0}| add......{1} " , DateTime.Now.ToString( " hh:mm:ss fff " ), Num); Thread.Sleep( 1000 ); } public void Init( int number) { if ( this .HasInit) return ; Num = number; Debug.Print( " {0}| init......{1} " , DateTime.Now.ToString( " hh:mm:ss fff " ), Num); Thread.Sleep( 1000 ); this .HasInit = true ; } }
场合1:必须先执行Init方法,后执行add方法,init和add在不同的线程中。
先Init后Add
OperationItem item = new OperationItem(); private void button1_Click( object sender, RoutedEventArgs e) { Thread thread1 = new Thread( new ThreadStart(InitOperation)); thread1.Name = " thread1 " ; thread1.Start(); Thread thread2 = new Thread( new ThreadStart(AddOperation)); thread2.Name = " thread2 " ; thread2.Start(); } void AddOperation() { Monitor.Enter(item); item.Add( 200 ); Monitor.Exit(item); } void InitOperation() { Monitor.Enter(item); item.Init( 100 ); Monitor.Exit(item); }
或者使用lock更清爽
Lock写法
OperationItem item = new OperationItem(); private void button1_Click( object sender, RoutedEventArgs e) { Thread thread1 = new Thread( new ThreadStart(InitOperation)); thread1.Name = " thread1 " ; thread1.Start(); Thread thread2 = new Thread( new ThreadStart(AddOperation)); thread2.Name = " thread2 " ; thread2.Start(); } void AddOperation() { lock (item) { item.Add( 200 ); } } void InitOperation() { lock (item) { item.Init( 100 ); } }
看看日志:
The thread 'thread1' (0x17b8) has exited with code 0 (0x0).
The thread 'thread2' (0x1110) has exited with code 0 (0x0).
如果没有lock或者Monitor
The thread 'thread2' (0x1410) has exited with code 0 (0x0).
The thread 'thread1' (0x15a8) has exited with code 0 (0x0).
这不乱套了吗,所以还是lock吧。
场合2:Init方法有可能被多次调用,但是需求决定Init只能执行一次。
init方法本身要加上判断,另外线程里面也要控制。
Monitor
private void button2_Click( object sender, RoutedEventArgs e) { Thread thread1 = new Thread( new ParameterizedThreadStart(InitOperation2)); thread1.Name = " thread1 " ; thread1.Start( 200 ); Thread thread2 = new Thread( new ParameterizedThreadStart(InitOperation2)); thread2.Name = " thread2 " ; thread2.Start( 500 ); } void InitOperation2( object data) { if (data == null ) return ; int ret = 0 ; Int32.TryParse(data.ToString(), out ret); // Monitor.Enter(item); // item.Init(ret); // Monitor.Exit(item); lock (item) { item.Init(ret); } }
日志:
The thread 'thread1' (0x3fc) has exited with code 0 (0x0).
The thread 'thread2' (0x93c) has exited with code 0 (0x0).
如果没有lock,悲剧了:
The thread 'thread1' (0x8e0) has exited with code 0 (0x0).
The thread 'thread2' (0xa04) has exited with code 0 (0x0).
场合3:对于耗时操作,我们的耐心是有限的,设置等待超时。
1 private void button3_Click( object sender, RoutedEventArgs e) 2 { 3 Thread thread1 = new Thread( new ParameterizedThreadStart(InitOperation3)); 4 thread1.Name = " thread1 " ; 5 thread1.Start( 200 ); 6 7 Thread thread2 = new Thread( new ParameterizedThreadStart(InitOperation3)); 8 thread2.Name = " thread2 " ; 9 thread2.Start( 500 ); 10 } 11 12 void InitOperation3( object data) 13 { 14 if (data == null ) return ; 15 int ret = 0 ; 16 Int32.TryParse(data.ToString(), out ret); 17 18 if (Monitor.TryEnter(item, 1200 )) 19 { 20 item.Add(ret); 21 Monitor.Exit(item); 22 } 23 }
留意第18行的1200,意思是等待的时间是1200毫秒,如果超出1200毫秒,就不执行了。
Add方法执行时,
线程会sleep1000毫秒,所以如果可以等待1200毫秒,还有戏,
1200ms 执行日志:
The thread 'thread1' (0x1bb8) has exited with code 0 (0x0).
The thread 'thread2' (0x1b40) has exited with code 0 (0x0).
如果只能等待800毫秒,那就没戏了,thread2直接退出。
800ms 执行日志:
The thread 'thread2' (0x9cc) has exited with code 0 (0x0).
The thread 'thread1' (0x1b2c) has exited with code 0 (0x0).
场合4:虽然可以 加锁,但是有时候还是需要交叉执行。
解释一下:
a.线程1执行add方法,执行前,要确保已经执行了Init,所以必须等待Init执行完成
b.线程2执行Init方法,尽管启动时间晚了两秒,但是启动后发现item的锁被让出,可以执行,所以马上Pulse,把Init的事情做了。
c.在线程2 Pulse的时候,线程1已经知道了线程有动静了,所以准备出动。
d.线程2 wait后,线程1马上拿过指挥权,继续完成它的工作。
1 private void button4_Click( object sender, RoutedEventArgs e) 2 { 3 Thread thread1 = new Thread( new ParameterizedThreadStart(InitOperation4)); 4 thread1.Name = " thread1 " ; 5 thread1.Start( 200 ); 6 Debug.Print( " {0}| start......{1} " , DateTime.Now.ToString( " hh:mm:ss fff " ), thread1.Name); 7 Thread.Sleep( 2000 ); 8 9 Thread thread2 = new Thread( new ParameterizedThreadStart(InitOperation4_1)); 10 thread2.Name = " thread2 " ; 11 Debug.Print( " {0}| start......{1} " , DateTime.Now.ToString( " hh:mm:ss fff " ), thread2.Name); 12 thread2.Start( 500 ); 13 } 14 15 void InitOperation4( object data) 16 { 17 if (data == null ) return ; 18 int ret = 0 ; 19 Int32.TryParse(data.ToString(), out ret); 20 21 lock (item) 22 { 23 Monitor.Wait(item); 24 item.Add(ret); 25 Monitor.Pulse(item); 26 } 27 Debug.Print( " {0}| finish ......{1} " , DateTime.Now.ToString( " hh:mm:ss fff " ), " InitOperation4 " ); 28 } 29 30 void InitOperation4_1( object data) 31 { 32 if (data == null ) return ; 33 int ret = 0 ; 34 Int32.TryParse(data.ToString(), out ret); 35 36 lock (item) 37 { 38 Monitor.Pulse(item); 39 item.Init(ret); 40 Monitor.Wait(item); 41 } 42 Debug.Print( " {0}| finish ......{1} " , DateTime.Now.ToString( " hh:mm:ss fff " ), " InitOperation4_1 " ); 43 }
所以,日志是:
The thread 'thread1' (0xa84) has exited with code 0 (0x0).
The thread 'thread2' (0x1310) has exited with code 0 (0x0).
结束:
Monitor真是个好东西呀,欢迎拍砖!
Demo在这里: https://files.cnblogs.com/xiaokang088/WpfMonitor.zip
另外,这位仁兄的解释更加清晰: http://hi.baidu.com/luoyuonline/blog/item/b10dfbeb56cd35d8d539c9ac.html
http://www.cnblogs.com/xiaokang088/archive/2012/05/09/2491782.html
作者: Leo_wl
出处: http://www.cnblogs.com/Leo_wl/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
版权信息