網站首頁 編程語言 正文
如果把生產消費想像成自動流水生產線的話,生產就是流水線的物料,消費就是某種設備對物料進行加工的行為,流水線就是隊列。
現在,要寫一個體現生產消費模式的泛型幫助類,比如叫ProducerConsumer<T>。
該類肯定會維護一個有關生產、物料的Queue<T>類型的字段,還存在一個有關消費、Action<T>類型的字段。
在ProducerConsumer類的構造函數中,為Action<T>類型的字段賦值,并開啟后臺有關消費的線程。
ProducerConsumer類肯定存在一個進隊列的方法,并且要保證在多線程情況下,同一時間只有一個生產或物料進入隊列。
ProducerConsumer類還存在一個有關消費的方法,并且保證在多線程情況下,同一時間只有一個生產或物料出列,并消費它。
另外,在生產或物料在出隊列的時候,可能會出現隊列中暫時沒有生產或物料的情況,這時候我們希望線程阻塞一下,這需要通過AutoResetEvent實現。AutoResetEvent的大致原理是:當生產或物料進入隊列的時候需要告訴AutoResetEvent一下,當隊列中暫時沒有生產或物料的時候,也需要告訴AutoResetEvent,讓它來阻塞線程。
//有關生產消費的泛型類
public class ProducerConsumer<T>
{
//用來存儲生產者的隊列
private readonly Queue<T> queue = new Queue<T>();
//鎖
private readonly object queueLocker = new object();
//消費行為
private readonly Action<T> consumerAction;
//出列的時候需要檢查隊列中是否有元素,如果沒有,需要阻塞
private readonly AutoResetEvent queueWaitHandle = new AutoResetEvent(false);
public ProducerConsumer(Action<T> consumerAction)
{
if (consumerAction == null)
{
throw new ArgumentNullException("consumerAction");
}
this.consumerAction = consumerAction;
//后臺開啟一個線程開始消費生產者
new Thread(this.ConsumeItems){IsBackground = true}.Start();
}
//進列
public void Enqueue(T item)
{
//確保同一時間只有一個生產者進列
lock (queueLocker)
{
queue.Enqueue(item);
//每次進列都要設置AutoResetEvent事件
this.queueWaitHandle.Set();
}
}
//消費動作
private void ConsumeItems()
{
while (true)
{
T nextItem = default(T);
//標志,確認隊列中的生產者是否存在
bool doesItemExist;
//確保同一時間只有一個生產者出列
lock (this.queueLocker)
{
//先確認隊列中的生產者是否存在
doesItemExist = this.queue.Count > 0;
if (doesItemExist)
{
nextItem = this.queue.Dequeue();
}
}
//如果生產者存在,才消費生產者
if (doesItemExist)
{
this.consumerAction(nextItem);
}
else//否則的話,再等等下一個隊列中的生產者
{
this.queueWaitHandle.WaitOne();
}
}
}
}
客戶端,針對多線程情形。
class Program
{
static void Main(string[] args)
{
//實例化一個int類型的生產消費實例
var producerConsumer = new ProducerConsumer<int>(i => Console.WriteLine("正在消費" + i));
Random random = new Random();
//開啟進隊列線程
var t1 = new Thread(() =>
{
for (int i = 0; i < 100; i++)
{
producerConsumer.Enqueue(i);
Thread.Sleep(random.Next(0,5));
}
});
var t2 = new Thread(() =>
{
for (int i = 0; i > -100; i--)
{
producerConsumer.Enqueue(i);
Thread.Sleep(random.Next(0, 5));
}
});
t1.Start();
t2.Start();
t1.Join();
t2.Join();
Thread.Sleep(50);
Console.ReadKey();
}
}
原文鏈接:https://www.cnblogs.com/darrenji/p/4523674.html
相關推薦
- 2022-04-14 如何解決error: failed to push some refs to ‘xxx(遠程庫)‘
- 2022-12-26 python3中的函數與參數及空值問題_python
- 2022-04-16 Android給通知channel靜音的方法實例_Android
- 2023-06-20 k8s應用監控探針詳解_云其它
- 2022-08-01 MongoDB創建與刪除數據庫_MongoDB
- 2022-06-08 淺談C#多線程下的調優_C#教程
- 2022-07-10 所有文章標題顯示為彩色,自定義指令v-rainbow
- 2023-01-31 Android位圖(圖片)加載引入的內存溢出問題詳細解析_Android
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支