關於執行緒同步與雙佇列效能

2021-06-27 16:44:27 字數 2849 閱讀 6795

關於執行緒同步與雙佇列效能 1

問題背景

2第一種方式,共享佇列

3第二種方式,雙佇列

4遇到的問題

2023年7

月12日星期日

這是在2023年3

月學習多執行緒程式設計時遇到的乙個問題。當時我寫了乙個**片段,其中兩個執行緒共享乙個佇列,乙個執行緒往佇列中寫資料,而另乙個執行緒從佇列中讀取資料。這是典型的生產者和消費者模型。但在這裡並不適合使用

semaphore

來做。

printf

輸出除錯資訊,

printf

是典型的

io操作會引起執行緒的切換,所以打出來的資訊也顯示了執行緒切換十分頻繁,幾乎每乙個資料入佇列後,就立即被另乙個執行緒搶占,並出佇列,佇列的長度一直為0或

1,處理效率非常低。

在本周四,我有乙個與架構師討論問題的機會,我便找架構師請教了這個問題。當時我給架構師描述了我的問題,也許是我自己基礎不好,架構師並不能很好的理解我的意思,但架構師根據我的描述給了我一些意見:

1、 如果是兩個執行緒互動,那麼除了加鎖之外,還可以使用原子操作(

atom

)來代替。 2、

如果兩個執行緒共享同乙個佇列,無可避免的就是其中乙個執行緒的在操作這個佇列時,另外乙個只能等著,效率很低。建議採用雙佇列技術,可以提高並行效率。這一點在後面的實驗中得到證實,使用雙佇列技術後效率提高了乙個數量級!

3、 作業系統一般

10ms-20ms

排程一次,所以應該盡量避免加鎖,因為加鎖會陷入核心。增加開銷。 4、

盡量避免使用

printf

這種同步

io,建議分配乙個大的緩衝,用於儲存輸出資訊,在寫滿後一次性輸出取出,這樣就避免頻繁的阻塞執行緒,導致排程頻繁。

根據架構師的建議,我在公司裡完成了這個程式,使用了兩種方式:共享佇列和雙佇列。

兩個執行緒共享乙個佇列,乙個往裡寫,乙個往外讀,我將寫入資料的那個執行緒叫做

writer

,讀出資料的那個執行緒叫做

reader

。這樣,

writer

執行緒在工作時,

reader

執行緒只能等待,

reader

執行緒在工作時寫執行緒只能等待。有人會說,可以多來幾個

reader

這樣可以使用

rwlock

,但根據應用場景,這裡就只有乙個消費者。這是一種最簡單的方式,也是最基本的方式,效率也很低。在這裡就不再討論這個方式的實現了。

兩個執行緒各自乙個佇列,

writer

執行緒往佇列

1寫資料,

reader

執行緒從佇列

2讀資料,而在佇列

2為空時,我將佇列

1中的資料拼接或交換到佇列

2中,這樣只有這個短暫的交換或拼接動作需要加鎖。

那麼這裡還有個問題,當佇列

1中有多少資料後拼接到佇列

2的效率是最高的呢?架構師給了乙個建議,先以

16個、

32個、

64個這樣的步進進行測試,直到找到效能最好的佇列拼接長度。而我最後沒有使用架構師給出的這個建議,而是模擬核心與用於空間之間的網路讀寫介面,這樣就把這個任務給了系統排程,大概策略如下: 1、

如果reader

執行緒發現佇列

2和佇列

1中都沒有資料,則陷入核心睡眠狀態,等待

event

喚醒。

2、writer

執行緒當往佇列

1中寫資料時,發現佇列

1和佇列

2都沒有資料,則認為

reader

執行緒肯定會等待在

event

上,則向

reader

執行緒出發

event

。由於作業系統規則,

event

類似於condition variable

,不會馬上被喚醒。所以

writer

有機會往佇列

1中繼續寫入一些資料,其實在多核

cpu情況下,

writer

一直可以往佇列

1中寫資料,除非佇列1被

reader

執行緒鎖住。 3、

reader

執行緒被event

喚醒之後,會幹以下事情 a)

如果佇列

2是空的,那麼就鎖住佇列

1和佇列

2,然後將佇列

1中的資料一次性拼接到佇列2中。

b)reader

執行緒開始讀取佇列

2中的資料,這裡需要注意的是如果是多核

cpu,這時

writer

執行緒可能正在往佇列

1中寫資料,這與

reader

執行緒處理是完全並行的,互不相干,所以雙佇列在多核

cpu上效率很高。 c)

如果reader

執行緒將佇列

2讀空之後,發現

writer

執行緒已經又向佇列

1中寫入了一些資料,那麼從步驟

a)開始重複執行。 d)

如果reader

執行緒將佇列

2讀空之後,發現佇列

1也是空的,那麼說明沒有資料了,這時

reader

執行緒又回到

event

上睡眠,等待

writer

執行緒的event

通知。

根據測試雙佇列的效能後,發現比共享佇列的效能高乙個數量級(

10倍),這些測試資料在公司裡的

intel p4 ht 3.0g 1.5gmem

上測試的出來的,而本文實在家裡寫的,所以資料沒有在這裡給出。

使用佇列讓執行緒同步

author ll ying python中的queue物件提供對執行緒同步的支援。使用queue物件可以實現多生產者和多消費者形成先進先出的佇列。每個生產者將資料一次存入佇列,而每個消費者依次從佇列中取出資料。importthreading importqueue python2中是queue模組...

執行緒程式設計 同步佇列

我們經常會採用生產者 消費者關係的兩個執行緒來處理乙個共享緩衝區的資料。例如一 個生產者執行緒接受使用者資料放入乙個共享緩衝區裡,等待乙個消費者執行緒對資料取出處理。但是如果緩衝區的太小而生產者和消費者兩個非同步執行緒的速度不同時,容 易出現乙個執行緒等待另乙個情況。為了盡可能的縮短共享資源並以相同...

佇列 雙端佇列與迴圈佇列

介面與類的繼承關係 雙端佇列 兩端都可以進行入隊和出隊操作的佇列 deque介面 linkedlist 實現類 方法 方法解釋 addfirst e ofterfirst e 入佇列 頭部元素 addlast e ofterlast e 入佇列 尾部元素 removefirst pool first...