同步代码区Monitor类的使用

来源:互联网 发布:win10网络受限 编辑:程序博客网 时间:2024/05/16 11:46
    Monitor类用于同步代码区,其方法是使用Monitor.Enter()方法获得一个锁,然后,使用Monitor.Exit()方法释放该锁。
一个线程获得锁,其他线程就要等到该锁释放后才能使用。一旦在代码区上获取了一个锁,就可以在Monitor.Enter()和Monitor.Exit()程序块内使用如下方法:
   Wait()-此方法用于释放对象上的锁,并暂停当前线程,直到它重新获得锁。
  Pulse()-此方法用于通知 正在队列中等待的线程,对象的状态已经改变。
  PulseAll()-此方法用于通知所有正在队列中等待的线程,对象的状态已经有了改变。
锁的机制主要是帮助帮助线程间的通信。如果一个线程进入对象的关键代码段,需要一定的条件才能存在,而另一线程可以在该代码中创建该条件,此时就需要这种机制。
在任一时刻,关键代码段只允许一具线程访问。当每一个线程进入代码段后,其他的线程就不能再进入了
下面是一个使用Enter()和Exit()地的示例:
using System;
using System.Threading;
using System.Runtime.CompilerServices;

namespace thread
{
    
public class MethodImpl
    
{
        
public class EnterExit
        
{
            
private int result = 0;
              
//锁定的代码段
            public void CriticalSection()
            
{
                
//Enter the Critical Section
                Monitor.Enter(this);

                Console.WriteLine(
"Entered Thread" + Thread.CurrentThread.GetHashCode());

                
for (int i = 1; i <= 5; i++)
                
{
                    Console.WriteLine(
"Result=" + result++ + "ThreadID" + Thread.CurrentThread.GetHashCode());
                    Thread.Sleep(
1000);
                }

                Console.WriteLine(
"Exiting Thread" + Thread.CurrentThread.GetHashCode());
                
//Exit the critical Section
                Monitor.Exit(this);
            }

            
public static void Main(string[] args)
            
{

                    EnterExit e 
= new EnterExit();
                    Thread ctl 
= new Thread(new ThreadStart(e.CriticalSection));
                    ctl.Start();
                    Thread ct2 
= new Thread(new ThreadStart(e.CriticalSection));
                    ct2 .Start();


               
            }


        }

    }

}


参数this指定应将锁保存在当前对象上。在需要锁定对象,不让其他线程访问它时,应把this指针作为参数和传递。



Wait()和Pulse()机制
Wait()和Pulse()机制用于线程间的交互。当在一个对象上执行Wait()时,正在访问该对象的线程就会进入等待状态。直到它得到一个唤醒信号。Pulse()和PulseAll()用于给等待线程发送信号。
注意,只能在Enter()和Exit()代码块中调用Wait()和Pulse()方法。

using System;
using System.Threading;
using System.Runtime.CompilerServices;

public class LockMe
{
}

public class WaitPulse1
{
    
private int result = 0;
    
private LockMe _1M;

    
public WaitPulse1() { }
    
public WaitPulse1(LockMe l)
    
{
        
this._1M = l;
    }

    
public void CriticalSection()
    
{

        Monitor.Enter(
this._1M);
        
//Enter the Critical Section
        Console.WriteLine("WaitPulse1:Entered Thread" + Thread.CurrentThread.GetHashCode());
        
for (int i = 1; i <= 5; i++)
        
{
            Monitor.Wait(
this._1M);
            Console.WriteLine(
"WaitPulse1:WokeUp");
            Console.WriteLine(
"WaitPulse1:Result=" + result++ + "ThreadID" + Thread.CurrentThread.GetHashCode());
            Monitor.Pulse(
this._1M);
        }

        Console.WriteLine(
"WaitPulse1:Eiting Thread" + Thread.CurrentThread.GetHashCode());
        
//Exit the Critical Section
       Monitor.Exit(this._1M);
       
    }

}


class WaitPulse2
{
    
private int result = 0;
    
internal LockMe _1M;
    
public WaitPulse2() { }
    
public WaitPulse2(LockMe l)
    
{
         
this._1M = l;
      
    }

    
public void CriticalSection()
    
{
        Monitor.Enter(
this._1M);
        Console.WriteLine(
"WaitPulse2:Entered Thread" + Thread.CurrentThread.GetHashCode());
        
for (int i = 1; i <= 5; i++)
        
{
            Monitor.Pulse(
this._1M);
            Console.WriteLine(
"WaitPulse2:Result=" + result++ + "ThreadID" + Thread.CurrentThread.GetHashCode());
            Monitor.Wait(
this._1M);
            Console.WriteLine(
"WaitPulse2:WokeUp");
        }

        Console.WriteLine(
"WaitPulse2:Exiging Thread" + Thread.CurrentThread.GetHashCode());
        
//Exit the Critical Section
        Monitor.Exit(this._1M);
    }

}

public class ClassForMain
{

    
public static void Main(string[] args)
    
{
        LockMe l 
= new LockMe();

        WaitPulse1 e1 
= new WaitPulse1(l);
        WaitPulse2 e2 
= new WaitPulse2(l);

        Thread t1 
= new Thread(new ThreadStart(e1.CriticalSection));
        t1.Start();

        Thread t2 
= new Thread(new ThreadStart(e2.CriticalSection));
        t2.Start();



    }


}
    


在Main()方法中,创建了一个名的l的LockMe的对象。
 接着创建两个类型为WaitPulse1和WaitPluse2的对象。注意,WaitPulse1中的LockMe对象实例与WaitPulse2中的LockMe对象实例相同,因为对象按引用被传递给它们各自的构造函数。初始化对象后,创建两个线程t1和t2,把它们分别传递给两个CriticalSection()方法。

假设WaitPulse1.CriticalSection()首先获得调用,则 线程t1通过LockMe对象上的锁定进入该方法的重要代码段,然后在For循环中执行Monitor.Wait()。执行Monitor.Wait()方法后,线程t1等待另一个线程的运行时通知 (Monitor.Pulse()),而被唤醒。锁定LockMe对象是希望在任一时刻只允许一个线程访问共享的LockMe实例。
注意,当线程执行Monitor.Wait()方法时,它会临时释放LockMe对象上的锁,以便其他线程可以访问它。线程t1进入等待状态后,线程t2就可自由访问LockMe对象。即LockMe对象是个独立的对象(WaitPulse1和WaitPuluse2),这两个线程也均指向同一个对象引用。线程t2获得LockMe对象上的锁后,进入WaitPulse2.CriticalSection方法。它一进入For循环,就给在LockMe对象上等待的线程(此时是t1)发送一个运行时通知(Monitor.Pulse()),它后进入睡眠状态。结果t1醒来,获得LckMe对象上的锁,接着线程t1访问result变量,并给在LockMe对象上等待的线程(此时是t2)发送一个运行时通知。这个循环 一直持续到For循环结束。
注意每个Enter()方法都应该伴随一个Exit()方法,否则程序将陷入死循环。
Enter()将一个对象作为其参数。如果该对象参数为null,方法变量或者值类型对象,如整型值,系统就会抛出一个异常。



















 

TryEnter()方法
Monitor类的TryEnter()方法非常类似于Enter()方法,它试图获得对象的独占锁,不过它不会像Enter()方法那样暂停。如果线程成功进入,则TryEnter()方法返回True。
例如ISP的拨号连接,假定有两个应用程序A和B,它们要使用同一个调制解调器拨号连接ISP。目前只有一个可用的调制解调器连接,而且一旦建立起连接,就无法知道连接上的程序会在网上呆多久。假定应用程序A先拨号连接上ISP,过了一段时间后,应用程序B也想拨号连接; 应用程序B可能会无限期地等待下去,因为我们不知道应用程序A要在网上呆多久。在这种情况下,应用程序B就可以使用TryEnter()来确定调制解调器是否已被其他应用程序(本例是A)锁定,而不是无限期地等待下去。

using System;
using System.Threading;
using System.Runtime.CompilerServices;



public class ClassForMain
{
    
public class TryEnter
    
{      //关键代码段
        public void CriticalSection()
        
{



            
if (Monitor.TryEnter(this1000))
            
{
                
try
                
{
                    Console.WriteLine(
"Entering...");
                    Console.WriteLine(
"Thread ID: " + Thread.CurrentThread.GetHashCode());
                    
for (int i = 1; i <= 5; i++)
                    
{
                        Thread.Sleep(
1000);
                        Console.WriteLine(
"Value:"++ " " + "Thread ID:"+Thread.CurrentThread.GetHashCode() + " ");
                       }

                   
                }

                
catch (Exception e)
                
{
                    Console.WriteLine(
"{0}", e.Message);
                }


                
finally
                
{
                    Monitor.Exit(
this);
                    Console.WriteLine(
"Exit!");
                }

            }

            
else
            
{
                Console.WriteLine(
"Fail to enter,I'll try later");
            }

        }


    }

        
//实例化两个线程,以“拨号”方式尝试进入代码区
    public static void Main(string[] args)
    
{
        TryEnter a 
= new TryEnter();
        Thread t1
=new Thread(new ThreadStart(a.CriticalSection));
        Thread t2 
= new Thread(new ThreadStart(a.CriticalSection));
        t1.Start();
        t2.Start();
       
    }
 

}
    


 


LOCK语句
Lock关键字可以用作Monitor方法的一个替换用法。下面例子输出结果与上面一样:
using System;
using System.Threading;
using System.Runtime.CompilerServices;



public class ClassForMain
{
    
public class LockWord
    
{      
        
public void CriticalSection()
        
{

            
lock(this)

            
{
                
//Enter the Critical Section
                Console.WriteLine("Entering...");
                Console.WriteLine(
"Thread ID: " + Thread.CurrentThread.GetHashCode());
                              
                    
for (int i = 1; i <= 5; i++)
                    
{
                       
                        Console.WriteLine(
"Value:"++ " " + "Thread ID:"+Thread.CurrentThread.GetHashCode() + " ");
                         Thread.Sleep(
1000);
                     }

                   Console.WriteLine(
"Exiting Thread"+Thread.CurrentThread.GetHashCode());
                }

          }

        
        
public static void Main(string[] args)
            
{
                LockWord e 
= new LockWord();

                Thread t1
=new Thread(new ThreadStart(e.CriticalSection));
                t1.Start();
                Thread t2 
= new Thread(new ThreadStart(e.CriticalSection));
                t2.Start();
             }
 
      }
    
   }