学习 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);
            }
        }

看看日志:

02:45:27 231| init......100

The thread 'thread1' (0x17b8) has exited with code 0 (0x0).

02:45:28 237| add......300

The thread 'thread2' (0x1110) has exited with code 0 (0x0).

如果没有lock或者Monitor

02:46:36 152| add......300

02:46:36 152| init......300

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);
            }
        }

日志:

02:50:46 234| init......200

The thread 'thread1' (0x3fc) has exited with code 0 (0x0).

The thread 'thread2' (0x93c) has exited with code 0 (0x0).

 

如果没有lock,悲剧了:

02:51:05 631| init......500

02:51:05 631| init......500

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 执行日志:

02:59:24 214| add......200

The thread 'thread1' (0x1bb8) has exited with code 0 (0x0).

02:59:25 220| add......700

The thread 'thread2' (0x1b40) has exited with code 0 (0x0).

 

如果只能等待800毫秒,那就没戏了,thread2直接退出。

800ms 执行日志:

03:00:11 219| add......200

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         }

 

 所以,日志是:

03:05:35 215| start......thread1

03:05:37 242| start......thread2

03:05:37 246| init......500

03:05:38 247| add......700

03:05:39 249| finish ......InitOperation4

The thread 'thread1' (0xa84) has exited with code 0 (0x0).

03:05:39 249| finish ......InitOperation4_1

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

posted @ 2012-05-09 15:13  xiaokang088  阅读(2929)  评论(1编辑  收藏  举报