多线程编程过程中,难免会涉及到资源共享的问题,在并发的线程中,如果不对线程加以控制,线程抢夺共享资源,对资源的读取和修改混乱,结果会导致不是我们想要的结果,这就需要引入线程同步的技术。
先演示下没有同步技术的案例:
class ThreadTT { int counter = 0; public void LockTest() { Thread t1 = new Thread(new ParameterizedThreadStart(Run)); Thread t2 = new Thread(new ParameterizedThreadStart(Run)); Thread t3 = new Thread(new ParameterizedThreadStart(Run)); t1.Start("t1"); t2.Start("t2"); t3.Start("t3"); } public void Run(object id) { for (int i = 0; i < 100; i++) { counter++; Thread.Sleep(100); counter--; Console.WriteLine("{0} counter {1}", id, counter); } } }
当执行LockTest() 方法之后,打印出的counter的值则是错乱的:
下面开始引入同步技术:
1.LOCK
class ThreadTT { int counter = 0; public void LockTest() { Thread t1 = new Thread(new ParameterizedThreadStart(Run)); Thread t2 = new Thread(new ParameterizedThreadStart(Run)); Thread t3 = new Thread(new ParameterizedThreadStart(Run)); t1.Start("t1"); t2.Start("t2"); t3.Start("t3"); } private object obj = new object(); public void Run(object id) { lock (obj) { for (int i = 0; i < 100; i++) { counter++; Thread.Sleep(100); counter--; Console.WriteLine("{0} counter {1}", id, counter); } } } }
再运行LockTest :
现在counter每次都只有一个线程占用;
2.Monitor
class ThreadTT { int counter = 0; public void LockTest() { Thread t1 = new Thread(new ParameterizedThreadStart(Run)); Thread t2 = new Thread(new ParameterizedThreadStart(Run)); Thread t3 = new Thread(new ParameterizedThreadStart(Run)); t1.Start("t1"); t2.Start("t2"); t3.Start("t3"); } private static object obj = new object(); public void Run(object id) { Monitor.Enter(obj); for (int i = 0; i < 10; i++) { counter++; Thread.Sleep(100); counter--; Console.WriteLine("{0} counter {1}", id, counter); } Monitor.Exit(obj); } }
Monitor.Wait() 释放线程锁,并阻塞线程,直到重新获取锁,否则之后的代码不再执行;
Monitor .pulse() 通知正在准备的线程,可以获取线程锁;
static void Main() { object obj = new object(); Thread t1 = new Thread(()=> { Monitor.Enter(obj); Console.WriteLine("t1 enter"); Console.WriteLine("t1.dosomething"); Monitor.Wait(obj); Thread.Sleep(2000); Console.WriteLine("t1 重新获取锁obj"); Monitor.Pulse(obj); Monitor.Exit(obj); Console.WriteLine("t1 exit"); }); Thread t2 = new Thread(()=> { Monitor.Enter(obj); Console.WriteLine("t2 enter"); Monitor.Pulse(obj); Console.WriteLine("t2 puslse"); Monitor.Wait(obj); Console.WriteLine("t2 重新获取obj"); Monitor.Exit(obj); Console.WriteLine("t2 exit"); }); t1.Start(); t2.Start(); Console.Read(); }
程序执行,首先进入t1,执行到Monitor.Wait(obj); 线程t1释放锁,并阻塞,开始进入线程t2,执行到Monitor.Wait(obj),线程t2阻塞,线程t1重新获取锁 执行到结束,并释放信号,t2接收到信号,继续执行结束
Monitor.Wait(obj,3000);
当前线程阻塞,等待3s钟,如果没有获取到锁,则继续执行下去
3.Mutex互斥锁
Mutex对象是一个同步基元,可以用来做线程间的同步。
若多个线程需要共享一个资源,可以在这些线程中使用Mutex同步基元。当某一个线程占用Mutex对象时,其他也需要占用Mutex的线程将处于挂起状态,Mutex 通常处于空闲状态,线程在使用的时候通过waitone()来获取一个可用的互斥,如果有,则获取互斥权限,执行线程,如果没有,则阻塞在那里直到等到信号;
private static Mutex mut = new Mutex(); private const int numIterations = 1; private const int numThreads = 3; static void Main() { for (int i = 0; i < numThreads; i++) { Thread newThread = new Thread(new ThreadStart(ThreadProc)); newThread.Name = String.Format("Thread{0}", i + 1); newThread.Start(); } Console.Read(); } private static void ThreadProc() { for (int i = 0; i < numIterations; i++) { UseResource(); } } // This method represents a resource that must be synchronized // so that only one thread at a time can enter. private static void UseResource() { // Wait until it is safe to enter. Console.WriteLine("{0} is requesting the mutex", Thread.CurrentThread.Name); mut.WaitOne(); Console.WriteLine("{0} has entered the protected area", Thread.CurrentThread.Name); // Place code to access non-reentrant resources here. // Simulate some work. Thread.Sleep(500); Console.WriteLine("{0} is leaving the protected area", Thread.CurrentThread.Name); // Release the Mutex. mut.ReleaseMutex(); Console.WriteLine("{0} has released the mutex", Thread.CurrentThread.Name); }
这里可以很清楚的看到,mutex每次只被一个线程占用,只有释放之后,另外的线程才可以进入;