操作系统PV操作——进程同步问题,C#实现

在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