在生產者-消費者模式中,我們常常會使用到隊列,這個隊列在多個線程共享訪問時存在互斥和競爭操作, 意味着每次訪問都要加鎖。如何更好的如何減少鎖競爭次數呢 ?今天要介紹的雙緩沖隊列就是個不錯的選擇。
雙緩沖隊列就是沖着同步/互斥的開銷來的。我們知道,在多個線程並發訪問同一個資源的時候,需要特別注意線程的同步問題。稍稍不注意,噢貨,程序結果不正確了。
原理
直接上圖:
鎖
在雙緩沖隊列中,鎖除了起到保護數據安全的作用來,還要承擔線程調度的任務。
- 雙隊列交換位置和任務入隊列都需要對當前隊列進行操作,因此,他們是互斥的操作。
- 消費操作放在單獨的線程中,在沒有任務進來時,需要將線程置為等待狀態。
使用兩個信號量,來調度入列隊和交換隊列的操作。同時,我們還需要一個信號量,在沒有任務入隊列時,阻塞整個消費線程。
主要使用 AutoResetEvent,ManualResetEvent,它們的具體使用可以看一下園子里的文章:
http://www.cnblogs.com/springyangwc/archive/2011/10/12/2208991.html
- AutoResetEvent.WaitOne()每次只允許一個線程進入,當某個線程得到信號后,AutoResetEvent會自動又將信號置為不發送狀態,則其他調用WaitOne的線程只有繼續等待,也就是說AutoResetEvent一次只喚醒一個線程;
- ManualResetEvent則可以喚醒多個線程,因為當某個線程調用了ManualResetEvent.Set()方法后,其他調用WaitOne的線程獲得信號得以繼續執行,而ManualResetEvent不會自動將信號置為不發送;
- 也就是說,除非手工調用了ManualResetEvent.Reset()方法,則ManualResetEvent將一直保持有信號狀態,ManualResetEvent也就可以同時喚醒多個線程繼續執行。
實現
我以一個簡單的示例來演示整個過程。
在生產線程中,入隊列一些字符串;使用消費線程,把這些字符串和它所在隊列編號打印出來:
看一下運行效果:
可以看到,在消費線程中,隊列交換使用。
下一篇,我們來實現一個對雙緩沖隊列的封裝。