作業系統學習:二 程序同步與互斥之生產者-消費者問題 .NetCore實現
目的
1. 掌握程序(執行緒)的同步與互斥。
2. 掌握生產者消費者問題的實現方法。
3. 掌握VC的多執行緒程式設計方法。
內容
本實驗要求設計並實現一個程序,該程序擁有3個生產者執行緒和1個消費者執行緒,它們使用10個不同的緩衝區。需要使用如下訊號量:
l 一個mutex訊號量,用以阻止生產者執行緒和消費者執行緒同時操作緩衝區佇列;
l 一個full訊號量,當生產者執行緒生產出一個物品時可以用它向消費者執行緒發出訊號;
l 一個empty訊號量,消費者執行緒釋放出一個空緩衝區時可以用它向生產者執行緒發出訊號;
生產者執行緒生產物品(通過等待一個時間模擬生產過程),然後將物品放置在一個空緩衝區中供消費者執行緒消費(通過將緩衝區陣列元素值設為產品編號模擬放入過程)。消費者執行緒從緩衝區中獲得物品,然後釋放緩衝區(通過將緩衝區陣列元素值設為0模擬取出過程)。當生產者執行緒生產物品時,如果沒有空緩衝區可用,那麼生產者執行緒必須等待消費者執行緒釋放出一個空緩衝區。當消費者執行緒消費物品時,如果沒有滿的緩衝區,那麼消費者執行緒將被阻塞,直到新的物品被生產出來。
根據給出的相關函式,實現生產者消費者問題。為了分析結果,要求用檔案儲存緩衝區變化過程。
虛擬碼
下面給出的是解決這一問題的部分程式碼和虛擬碼,後面附上完整程式碼
#include <windows.h>//大多數API函式都在這個標頭檔案中定義 #include <stdio.h> #include <iostream.h> const unsigned short SIZE_OF_BUFFER = 10;//緩衝區個數 unsigned short ProductID = 0;//生產的產品號 unsigned short ConsumeID = 0;//將被消耗的產品號 unsigned short in = 0;//產品進緩衝區時的緩衝區下標 unsigned short out = 0;//產品出緩衝區時的緩衝區下標 //緩衝區是個迴圈佇列 int g_buffer[SIZE_OF_BUFFER];//用整型陣列模擬緩衝區 boolg_continue = true;//當g_continue = false結束程式 HANDLE g_hMutex;// g_hMutex是互斥訊號量的控制代碼 HANDLE g_hEmptySemaphore;//即empty訊號量的控制代碼 HANDLE g_hFullSemaphore;//即full訊號量的控制代碼 DWORD WINAPI Producer(LPVOID);//生產者執行緒 DWORD WINAPI Consumer(LPVOID);//消費者執行緒 //請完成主函式,主函式中要完成建立各訊號量物件和建立各執行緒的工作 main(…) {… //建立生產者和消費者執行緒 CreateThread(…, Producer, …); CreateThread(…, Consumer, …); … } //請完成模擬生產者把新生產的產品放入緩衝區的過程 void AddToBuffer() { ... } //請完成模擬消費者從緩衝區中取出一個產品的過程 void TakeFromBuffer() { … } //請根據虛擬碼完成生產者執行緒函式 Producer() { …//獲取執行緒ID while(TRUE) {//模擬生產過程,生產一個物品 sleep(rand);//可以給出時間引數,1000為1秒,休眠時間 P(empty);//請求一個空緩衝區 //操作緩衝區池 P(mutex); …//輸出執行緒ID,這樣可以看出是哪一個執行緒在工作 AddToBuffer();//將物品放置在一個空緩衝區中 V(mutex); V(full);//用訊號通知一個滿的緩衝區 } return 0; } //請根據虛擬碼完成消費者執行緒函式 Consumer() { …//獲取執行緒ID while(TRUE) { P(full);//請求一個滿緩衝區 //操作緩衝區池 P(mutex); …//輸出執行緒ID,這樣可以看出是哪一個執行緒在工作 TakeFromBuffer();//從一個滿緩衝區中取產品 V(mutex); V(empty);//用訊號通知一個空的緩衝區 sleep(rand);//休眠時間,模擬消費產品 } return 0; }
實現程式碼
using System; using System.Dynamic; using System.Threading; namespace OperatingSystemExperiment.Exp2 { /// <summary> /// 生產者-消費者問題 /// 使用 P-V 操作解決同步和互斥問題 /// 本實驗要求設計並實現一個程序,該程序擁有3個生產者執行緒和1個消費者執行緒,它們使用10個不同的緩衝區。 /// </summary> public static class Main { private static int[] _buffer = new int[10]; /// <summary> /// 是否繼續執行 /// </summary> private static bool _continueRun = true; /// <summary> /// 是否鎖定緩衝區 /// </summary> private static bool _isLock = false; /// <summary> /// 產品號 /// </summary> private static int _productId = 0; /// <summary> /// 訊號列舉 /// </summary> private enum SemaphoreEnum { /// <summary> /// 互斥訊號量,用以阻止生產者執行緒和消費者執行緒同時操作緩衝區佇列 /// </summary> Mutex, /// <summary> /// 當生產者執行緒生產出一個物品時可以用它向消費者執行緒發出訊號 /// </summary> Full, /// <summary> /// 消費者執行緒釋放出一個空緩衝區時可以用它向生產者執行緒發出訊號 /// </summary> Empty } public static void Do() { ThreadStart producer = () => { while (_continueRun) { Thread.Sleep(1200); // 請求空緩衝區 var emptyBufferId = GetEmptyBuffer(); // 沒有空緩衝區,繼續等 if (emptyBufferId == -1) continue; // 緩衝區鎖定,等待 if (_isLock) continue; P(SemaphoreEnum.Empty); P(SemaphoreEnum.Mutex); Console.WriteLine("生產執行緒 {0} 工作中", Thread.CurrentThread.ManagedThreadId); AddToBuffer(emptyBufferId, ++_productId); Console.WriteLine("Produce the {0} product to buffer.", _productId); // 輸出緩衝區內容 var nextIn = GetEmptyBuffer(); var nextOut = GetFullBuffer(); for (var i = 0; i < _buffer.Length; i++) { if (i == nextOut) Console.WriteLine("{0}: {1} <- 下一個可取出產品消費的地方", i, _buffer[i]); else if (i == nextIn) Console.WriteLine("{0}: {1} <- 可放下一個產品的位置", i, _buffer[i]); else Console.WriteLine("{0}: {1}", i, _buffer[i]); } V(SemaphoreEnum.Mutex); // 用訊號通知一個消費者執行緒有一個滿的緩衝區 V(SemaphoreEnum.Full); } }; // 1個監視執行緒 new Thread(() => { while (_continueRun) { if (_productId > 20) _continueRun = false; } }).Start(); // 3個生產者 new Thread(producer).Start(); new Thread(producer).Start(); new Thread(producer).Start(); // 1個消費者 new Thread(() => { while (_continueRun) { Thread.Sleep(200); // 請求一個滿的緩衝區 var fullBufferId = GetFullBuffer(); if (fullBufferId == -1) continue; // 緩衝區鎖定則繼續等待 if (_isLock) continue; P(SemaphoreEnum.Full); // 操作緩衝區池 P(SemaphoreEnum.Mutex); Console.WriteLine("消費者執行緒 {0} 工作", Thread.CurrentThread.ManagedThreadId); var productId = TakeFromBuffer(fullBufferId); Console.WriteLine("正在消費產品 {0}", productId); V(SemaphoreEnum.Mutex); // 用訊號通知一個空的緩衝區 V(SemaphoreEnum.Empty); } }).Start(); } /// <summary> /// 生產者把新生產的產品放入緩衝區 /// </summary> /// <returns>是否成功放入,沒有空緩衝區的時候不成功</returns> private static bool AddToBuffer(int position, int product) { if (_buffer[position] != 0) return false; _buffer[position] = product; return true; } /// <summary> /// 獲取一個空的緩衝區,都是滿的則返回-1 /// </summary> /// <returns>空緩衝區的編號</returns> private static int GetEmptyBuffer() { for (var i = 0; i < _buffer.Length; i++) { if (_buffer[i] == 0) { return i; } } return -1; } /// <summary> /// 獲取一個滿的緩衝區,都是空的則返回-1 /// </summary> /// <returns>滿緩衝區的編號</returns> private static int GetFullBuffer() { for (var i = 0; i < _buffer.Length; i++) { if (_buffer[i] != 0) { return i; } } return -1; } /// <summary> /// 消費者從緩衝區中取出一個產品 /// </summary> /// <returns>產品id</returns> private static int TakeFromBuffer(int position) { var temp = _buffer[position]; _buffer[position] = 0; return temp; } /// <summary> /// 申請資源操作 /// </summary> /// <param name="s"></param> private static void P(SemaphoreEnum s) { switch (s) { case SemaphoreEnum.Mutex: _isLock = true; break; case SemaphoreEnum.Full: break; case SemaphoreEnum.Empty: break; default: throw new ArgumentOutOfRangeException(nameof(s), s, null); } } /// <summary> /// 釋放資源操作 /// </summary> /// <param name="s"></param> private static void V(SemaphoreEnum s) { switch (s) { case SemaphoreEnum.Mutex: _isLock = false; break; case SemaphoreEnum.Full: break; case SemaphoreEnum.Empty: break; default: throw new ArgumentOutOfRangeException(nameof(s), s, null); } } } }
執行結果
生產執行緒 4 工作中 Produce the 1 product to buffer. 0: 1 <- 下一個可取出產品消費的地方 1: 0 <- 可放下一個產品的位置 2: 0 3: 0 4: 0 5: 0 6: 0 7: 0 8: 0 9: 0 消費者執行緒 7 工作 正在消費產品 1 生產執行緒 5 工作中 Produce the 2 product to buffer. 0: 2 <- 下一個可取出產品消費的地方 1: 0 <- 可放下一個產品的位置 2: 0 3: 0 4: 0 5: 0 6: 0 7: 0 8: 0 9: 0 消費者執行緒 7 工作 正在消費產品 2 生產執行緒 4 工作中 Produce the 3 product to buffer. 0: 3 <- 下一個可取出產品消費的地方 1: 0 <- 可放下一個產品的位置 2: 0 3: 0 4: 0 5: 0 6: 0 7: 0 8: 0 9: 0 消費者執行緒 7 工作 正在消費產品 3 生產執行緒 6 工作中 Produce the 4 product to buffer. 0: 4 <- 下一個可取出產品消費的地方 1: 0 <- 可放下一個產品的位置 2: 0 3: 0 4: 0 5: 0 6: 0 7: 0 8: 0 9: 0 消費者執行緒 7 工作 正在消費產品 4 生產執行緒 4 工作中 Produce the 5 product to buffer. 0: 5 <- 下一個可取出產品消費的地方 1: 0 <- 可放下一個產品的位置 2: 0 3: 0 4: 0 5: 0 6: 0 7: 0 8: 0 9: 0 消費者執行緒 7 工作 正在消費產品 5 生產執行緒 5 工作中 Produce the 6 product to buffer. 0: 6 <- 下一個可取出產品消費的地方 1: 0 <- 可放下一個產品的位置 2: 0 3: 0 4: 0 5: 0 6: 0 7: 0 8: 0 9: 0 生產執行緒 4 工作中 Produce the 7 product to buffer. 0: 6 <- 下一個可取出產品消費的地方 1: 7 2: 0 <- 可放下一個產品的位置 3: 0 4: 0 5: 0 6: 0 7: 0 8: 0 9: 0 消費者執行緒 7 工作 正在消費產品 6 消費者執行緒 7 工作 正在消費產品 7 生產執行緒 6 工作中 Produce the 8 product to buffer. 0: 8 <- 下一個可取出產品消費的地方 1: 0 <- 可放下一個產品的位置 2: 0 3: 0 4: 0 5: 0 6: 0 7: 0 8: 0 9: 0 生產執行緒 5 工作中 Produce the 9 product to buffer. 0: 8 <- 下一個可取出產品消費的地方 1: 9 2: 0 <- 可放下一個產品的位置 3: 0 4: 0 5: 0 6: 0 7: 0 8: 0 9: 0 生產執行緒 4 工作中 Produce the 10 product to buffer. 0: 8 <- 下一個可取出產品消費的地方 1: 9 2: 10 3: 0 <- 可放下一個產品的位置 4: 0 5: 0 6: 0 7: 0 8: 0 9: 0 消費者執行緒 7 工作 正在消費產品 8 消費者執行緒 7 工作 正在消費產品 9 消費者執行緒 7 工作 正在消費產品 10 生產執行緒 6 工作中 Produce the 11 product to buffer. 0: 11 <- 下一個可取出產品消費的地方 1: 0 <- 可放下一個產品的位置 2: 0 3: 0 4: 0 5: 0 6: 0 7: 0 8: 0 9: 0 生產執行緒 4 工作中 Produce the 12 product to buffer. 0: 11 <- 下一個可取出產品消費的地方 1: 12 2: 0 <- 可放下一個產品的位置 3: 0 4: 0 5: 0 6: 0 7: 0 8: 0 9: 0 消費者執行緒 7 工作 正在消費產品 11 消費者執行緒 7 工作 正在消費產品 12 生產執行緒 5 工作中 Produce the 13 product to buffer. 0: 13 <- 下一個可取出產品消費的地方 1: 0 <- 可放下一個產品的位置 2: 0 3: 0 4: 0 5: 0 6: 0 7: 0 8: 0 9: 0 生產執行緒 6 工作中 Produce the 14 product to buffer. 0: 13 <- 下一個可取出產品消費的地方 1: 14 2: 0 <- 可放下一個產品的位置 3: 0 4: 0 5: 0 6: 0 7: 0 8: 0 9: 0 生產執行緒 4 工作中 Produce the 15 product to buffer. 0: 13 <- 下一個可取出產品消費的地方 1: 14 2: 15 3: 0 <- 可放下一個產品的位置 4: 0 5: 0 6: 0 7: 0 8: 0 9: 0 消費者執行緒 7 工作 正在消費產品 13 消費者執行緒 7 工作 正在消費產品 14 消費者執行緒 7 工作 正在消費產品 15 生產執行緒 5 工作中 Produce the 16 product to buffer. 0: 16 <- 下一個可取出產品消費的地方 1: 0 <- 可放下一個產品的位置 2: 0 3: 0 4: 0 5: 0 6: 0 7: 0 8: 0 9: 0 生產執行緒 6 工作中 Produce the 17 product to buffer. 0: 16 <- 下一個可取出產品消費的地方 1: 17 2: 0 <- 可放下一個產品的位置 3: 0 4: 0 5: 0 6: 0 7: 0 8: 0 9: 0 生產執行緒 4 工作中 Produce the 18 product to buffer. 0: 16 <- 下一個可取出產品消費的地方 1: 17 2: 18 3: 0 <- 可放下一個產品的位置 4: 0 5: 0 6: 0 7: 0 8: 0 9: 0 消費者執行緒 7 工作 正在消費產品 16 消費者執行緒 7 工作 正在消費產品 17 消費者執行緒 7 工作 正在消費產品 18 生產執行緒 5 工作中 Produce the 19 product to buffer. 0: 19 <- 下一個可取出產品消費的地方 1: 0 <- 可放下一個產品的位置 2: 0 3: 0 4: 0 5: 0 6: 0 7: 0 8: 0 9: 0 生產執行緒 4 工作中 Produce the 20 product to buffer. 0: 19 <- 下一個可取出產品消費的地方 1: 20 2: 0 <- 可放下一個產品的位置 3: 0 4: 0 5: 0 6: 0 7: 0 8: 0 9: 0 消費者執行緒 7 工作 正在消費產品 19 消費者執行緒 7 工作 正在消費產品 20 生產執行緒 5 工作中 Produce the 21 product to buffer. 0: 21 <- 下一個可取出產品消費的地方 1: 0 <- 可放下一個產品的位置 2: 0 3: 0 4: 0 5: 0 6: 0 7: 0 8: 0 9: 0 生產執行緒 4 工作中 Produce the 22 product to buffer. 0: 21 <- 下一個可取出產品消費的地方 1: 22 2: 0 <- 可放下一個產品的位置 3: 0 4: 0 5: 0 6: 0 7: 0 8: 0 9: 0 消費者執行緒 7 工作 正在消費產品 21 生產執行緒 6 工作中 Produce the 23 product to buffer. 0: 23 <- 下一個可取出產品消費的地方 1: 22 2: 0 <- 可放下一個產品的位置 3: 0 4: 0 5: 0 6: 0 7: 0 8: 0 9: 0
About

Learn more on my WeChat Official Account:DealiAxy
Every post was in my blog: ofollow,noindex">blog.deali.cn