[原創]C#編寫的多生產者多消費者同步問題

hippoz發表於2004-09-29

// 多個生產者和多個消費者,能生產n個產品的情況

using System;
using System.Threading;

public class HoldIntegerSynchronized{

        private int[] buffer;           //緩衝區

        private int occupiedBufferCount = 0;
        
        private int readPosition = 0 , writePosition = 0;
        //下一個讀到的位置和寫到的位置
        
        public HoldIntegerSynchronized(int capacity){
                buffer = new int[capacity];
        }
        
        public int BufferSize{
                get{
                        return buffer.Length;
                }
        }

        public int Buffer{
                get{
                        int bufferCopy;
                        // 加鎖
                        lock(this){
                                while(occupiedBufferCount == 0){        //多個消費者,所以此處改用while
                                        Console.WriteLine(Thread.CurrentThread.Name + " tries to read. ");
                                        DisplayState("Buffer Empty. " + Thread.CurrentThread.Name + " waits.");
                                        Monitor.Wait(this);
                                        // 為臨界區之外等待的生產者放行,讓他來"生產"
                                        // 一直到生產者生產結束,呼叫了Monitor.PauseAll()
                                        // 才能繼續執行下去,此時,消費者自動重新獲得this的鎖
                                }

                                --occupiedBufferCount;

                                bufferCopy = buffer[readPosition];

                                readPosition = (readPosition + 1) % buffer.Length;                              
                                
                                DisplayState(Thread.CurrentThread.Name + " reads " + bufferCopy);


                                // 通知,讓等待的 生產者執行緒 進入Started狀態,如果生產者處於臨界區之外,這句話執行完後他仍然在臨界區之外
                                Monitor.PulseAll(this);

                        // 釋放鎖
                        }//lock
                        return bufferCopy;
                }

                set{
                        // 加鎖
                        lock(this){
                                while(occupiedBufferCount == buffer.Length){
                                        Console.WriteLine(Thread.CurrentThread.Name + " tries to write. ");
                                        DisplayState("Buffer Full. " + Thread.CurrentThread.Name + " waits.");
                                        Monitor.Wait(this);    
                                        // 為臨界區之外等待消費者放行,讓他來"消費"
                                        // 一直到消費者呼叫了Monitor.Pause()
                                        // 才能繼續執行下去,此時,生產者自動重新獲得this的鎖
                                }

                                buffer[writePosition] = value;
                                
                                ++occupiedBufferCount;
                                
                                writePosition = (writePosition + 1) % buffer.Length;

                                DisplayState(Thread.CurrentThread.Name + " writes " + value);

                                // 通知,讓Wait狀態的 消費者 進入Started狀態,如果消費者處於臨界區之外,這句話執行完後他仍然在臨界區之外
                                Monitor.PulseAll(this);

                        // 釋放鎖
                        }
                }
        }

        public void DisplayState(string operation){
                Console.Write("{0,-35}",operation);
                for(int i = 0; i < BufferSize; i++ ){
                        int a = readPosition;
                        int b = writePosition;

                        if( a <= i && i < b) {
                                Console.Write("{0,-9}",buffer[i]);
                        }else if( b < a && !( b <= i && i < a ) ){
                                Console.Write("{0,-9}",buffer[i]);
                        }else if( occupiedBufferCount == BufferSize){
                                Console.Write("{0,-9}",buffer[i]);
                        }else{
                                Console.Write("{0,-9}","");
                        }

                }
                Console.WriteLine("{0}/r/n",occupiedBufferCount);
        }
}

class Producer{
        private HoldIntegerSynchronized sharedLocation;
        private Random randomSleepTime;

        public Producer(HoldIntegerSynchronized shared,Random random){
                sharedLocation = shared;
                randomSleepTime = random;
        }
        
        public void Produce(){
                for (int count=0; count<3; count++)     {
                        Thread.Sleep(randomSleepTime.Next(1,2000));
                        sharedLocation.Buffer = randomSleepTime.Next(5,10);
                }
                Console.WriteLine(Thread.CurrentThread.Name + " done producing./r/nTerminating " + Thread.CurrentThread.Name + "./r/n");
        }
}

class Consumer{
        private HoldIntegerSynchronized sharedLocation;
        private Random randomSleepTime;

        public Consumer(HoldIntegerSynchronized shared,Random random){
                sharedLocation = shared;
                randomSleepTime = random;
        }
        
        public void Consume(){
                int sum = 0;
                for (int count=0; count<4; count++)     {
                        Thread.Sleep(randomSleepTime.Next(1,2000));
                        sum += sharedLocation.Buffer;
                }
                Console.WriteLine(Thread.CurrentThread.Name + " read values totaling:" + sum + "/r/nTerminating " + Thread.CurrentThread.Name + ".");
        }      
}

class SharedCell{
        static void Main(string[] args){
                HoldIntegerSynchronized holdInteger = new HoldIntegerSynchronized(5);
                Random random = new Random();
                        
                Thread[] producerThreads = new Thread[4];
                Thread[] consumerThreads = new Thread[3];

                Console.Write("{0,-35}","Operation");
                for(int i = 0;i < holdInteger.BufferSize;i++){
                        Console.Write("{0,-9}","Elem " + i);
                }
                Console.WriteLine("Occupied Count/r/n");

                for(int i = 0; i < producerThreads.Length;i++){
                        Producer producer = new Producer(holdInteger,random);
                        producerThreads[i] = new Thread(new ThreadStart(producer.Produce));
                        producerThreads[i].Name = "Producer No." + i;
                }

                for(int i = 0; i < consumerThreads.Length;i++){
                        Consumer consumer = new Consumer(holdInteger,random);
                        consumerThreads[i] = new Thread(new ThreadStart(consumer.Consume));
                        consumerThreads[i].Name = "Consumer No." + i;
                }

                for(int i = 0; i < producerThreads.Length;i++){
                        producerThreads[i].Start();
                }

                for(int i = 0; i < consumerThreads.Length;i++){
                        consumerThreads[i].Start();
                }
        }
}

相關文章