在C#中,用于同步的主要是Mutex类与Semaphore类。位于System.Threading命名空间中。
在这两个种对象的方法中,P操作对应的是WaitOne()方法,V操作对应的是ReleaseMutex()与Release()方法。
下面是用C#解决几大PV操作经典问题及其变形的代码。
一、生产者消费者问题
1.最简单的情况:一个生产者,一个消费者,共用一个缓冲区进行生产消费。
1 using System; 2 using System.Threading; 3 4 namespace ProducerCustomer1_SingleBuffer 5 { 6 class ProducerCustomer1_SingleBuffer 7 { 8 static Mutex mutex = new Mutex(); 9 static void Main(string[] args) 10 { 11 new Thread(new ThreadStart(Producer)).Start(); 12 new Thread(new ThreadStart(Customer)).Start(); 13 Console.Read(); 14 } 15 16 private static void Producer() 17 { 18 while (true) 19 { 20 mutex.WaitOne(); 21 Console.WriteLine("Producer is working!"); 22 mutex.ReleaseMutex(); 23 Thread.Sleep(400); 24 } 25 } 26 27 private static void Customer() 28 { 29 while (true) 30 { 31 mutex.WaitOne(); 32 Console.WriteLine("customer is working"); 33 mutex.ReleaseMutex(); 34 Thread.Sleep(400); 35 } 36 } 37 } 38 }
2.现在稍微复杂一些,生产者与消费者共用一个大小为n的环形缓冲区。本例中n取10.
1 using System; 2 using System.Threading; 3 4 namespace ProducerCustomer2_MultiBuffer 5 { 6 class ProducerCustomer2_MultiBuffer 7 { 8 static Mutex mutex = new Mutex(); 9 static Semaphore empty = new Semaphore(10,10); 10 static Semaphore full = new Semaphore(0,10); 11 static int[] buffer = new int[10]; 12 static Random rand = new Random(); 13 14 static void Main(string[] args) 15 { 16 new Thread(new ThreadStart(Producer)).Start(); 17 new Thread(new ThreadStart(Customer)).Start(); 18 Console.Read(); 19 } 20 21 private static void Producer() 22 { 23 uint ppointer=0; 24 int temp; 25 while (true) 26 { 27 //!!!!Attention!!!!!NEVER MIX Orders 28 //如果empty与mutex的顺序反了,就会发生死锁! 29 empty.WaitOne(); 30 mutex.WaitOne(); 31 temp = rand.Next(1, 100); 32 buffer[ppointer] = temp; 33 Console.WriteLine("Producer works at {0} with {1}",ppointer,temp); 34 ppointer = (ppointer + 1)%10; 35 mutex.ReleaseMutex(); 36 full.Release(); 37 Thread.Sleep(400); 38 } 39 } 40 41 private static void Customer() 42 { 43 uint cpointer=0; 44 int temp; 45 while (true) 46 { 47 full.WaitOne(); 48 mutex.WaitOne(); 49 temp = rand.Next(1, 100); 50 temp = buffer[cpointer]; 51 Console.WriteLine("Customer gains at {0} with {1}", cpointer, temp); 52 cpointer = (cpointer + 1) % 10; 53 mutex.ReleaseMutex(); 54 empty.Release(); 55 Thread.Sleep(400); 56 } 57 } 58 } 59 }
3.问题再升级:
桌上有一个空盘子,只允许放一个水果。爸爸可以放苹果。也可以放橘子。儿子要吃苹果,女儿要吃橘子。试实现之。
1 using System; 2 using System.Threading; 3 4 namespace ProducerCustomer3_Fruit 5 { 6 internal class ProducerCustomer3_Fruit 7 { 8 private static Mutex mutex = new Mutex(); 9 private static Semaphore Sempty = new Semaphore(1, 1); 10 private static Semaphore Sorange = new Semaphore(0, 1); 11 private static Semaphore Sapple = new Semaphore(0, 1); 12 13 private static Fruit buffer = 0; 14 private static Random rand = new Random(); 15 16 public enum Fruit 17 { 18 Empty, 19 Apple, 20 Orange 21 }; 22 23 private static void Main(string[] args) 24 { 25 new Thread(new ThreadStart(Papa)).Start(); 26 new Thread(new ThreadStart(Son)).Start(); 27 new Thread(new ThreadStart(Daughter)).Start(); 28 Console.Read(); 29 } 30 31 private static void Papa() 32 { 33 while (true) 34 { 35 int temp = 0; 36 Sempty.WaitOne(); 37 mutex.WaitOne(); 38 temp = rand.Next(1,3); 39 if (temp == 1) 40 { 41 Console.WriteLine("Papa put an apple"); 42 buffer = Fruit.Apple; 43 mutex.ReleaseMutex(); 44 Sapple.Release(); 45 } 46 else 47 { 48 Console.WriteLine("Papa put an Orange"); 49 buffer = Fruit.Orange; 50 mutex.ReleaseMutex(); 51 Sorange.Release(); 52 } 53 Thread.Sleep(400); 54 } 55 } 56 57 private static void Son() 58 { 59 while (true) 60 { 61 Sapple.WaitOne(); 62 mutex.WaitOne(); 63 Console.WriteLine("Son eat an {0}", buffer); 64 mutex.ReleaseMutex(); 65 Sempty.Release(); 66 Thread.Sleep(400); 67 } 68 } 69 70 private static void Daughter() 71 { 72 while (true) 73 { 74 Sorange.WaitOne(); 75 mutex.WaitOne(); 76 Console.WriteLine("Daughter eat an {0}", buffer); 77 mutex.ReleaseMutex(); 78 Sempty.Release(); 79 Thread.Sleep(400); 80 } 81 } 82 } 83 }
二、哲学家进餐问题
五个哲学家围着桌子共同进餐,每个哲学家两侧各有一支筷子。试设计同步算法,使哲学家都能吃上饭。
1 using System; 2 using System.Threading; 3 4 namespace PhilosophersProblem 5 { 6 class PhilosophersProblem 7 { 8 static Semaphore[] chopsticks = { new Semaphore(1, 1) 9 , new Semaphore(1, 1) 10 , new Semaphore(1, 1) 11 , new Semaphore(1, 1), 12 new Semaphore(1, 1), }; 13 14 static void Main(string[] args) 15 { 16 for (int i = 0; i < 5; i++) 17 { 18 new Thread(new ParameterizedThreadStart(philosopher)).Start(i); 19 } 20 Console.Read(); 21 } 22 23 private static void philosopher(object input) 24 { 25 int i = (int)input; 26 chopsticks[i].WaitOne(); 27 chopsticks[(i + 1)%5].WaitOne(); 28 29 Console.WriteLine("Philosopher{0}: I am eating",i); 30 Thread.Sleep(1000); 31 32 chopsticks[i].Release(); 33 chopsticks[(i + 1)%5].Release(); 34 } 35 } 36 }
对于此类问题,应尽量使用信号量集或者and型信号量以避免死锁。
三、读者写者问题
一个文件,允许多个读者同时读取,只能允许一个写者同时写,写者写的时候不能读。试实现之。
1 using System; 2 using System.Threading; 3 4 namespace ReaderWriter 5 { 6 class ReaderWriter 7 { 8 9 //Warning : Some Error will occur if more than one Reader is newed 10 //Using Semaphore can solve this however here I take mutex as example to show the concept. 11 static int Readcount =0 ; 12 static Mutex WMutex = new Mutex(); 13 static Mutex RMutex = new Mutex(); 14 static int buffer = 0; 15 static Random rand = new Random(); 16 17 static void Main(string[] args) 18 { 19 new Thread(new ThreadStart(Writer)).Start(); 20 new Thread(new ThreadStart(Reader)).Start(); 21 new Thread(new ThreadStart(Reader)).Start(); 22 23 24 Console.Read(); 25 } 26 27 private static void Reader() 28 { 29 while (true) 30 { 31 RMutex.WaitOne(); 32 if (Readcount == 0) 33 { 34 WMutex.WaitOne(); 35 } 36 Readcount++; 37 RMutex.ReleaseMutex(); 38 39 Console.WriteLine("I have read this:{0}", buffer.ToString()); 40 41 RMutex.WaitOne(); 42 Readcount--; 43 if (Readcount == 0) 44 { 45 WMutex.ReleaseMutex(); 46 } 47 RMutex.ReleaseMutex(); 48 Thread.Sleep(300); 49 } 50 } 51 52 private static void Writer() 53 { 54 while (true) 55 { 56 WMutex.WaitOne(); 57 buffer = rand.Next(1, 10); 58 Console.WriteLine("Write {0}",buffer); 59 WMutex.ReleaseMutex(); 60 Thread.Sleep(1000); 61 } 62 } 63 } 64 }
Rohan
2014-1-7