簡體   English   中英

C++ 在具有同步隊列的線程中等待通知

[英]C++ wait notify in threads with synchronized queues

我有一個這樣結構的程序:一個線程接收任務並將它們寫入輸入隊列,多個線程處理它們並寫入 output 隊列,一個用它的結果進行響應。 當隊列為空時,線程會休眠幾毫秒。 隊列內部有互斥鎖,推入執行 lock(),彈出執行 try_lock(),如果隊列中沒有任何內容則返回。

這是處理線程,例如:

//working - atomic bool
while (working) {
    if (!inputQue_->pop(msg)) {
        std::this_thread::sleep_for(std::chrono::milliseconds(200));
        continue;
    } else {
        string reply = messageHandler_->handle(msg);
        if (!reply.empty()) {
            outputQue_->push(reply);
        }
    }
}

我不喜歡的是,從收到任務到響應的時間,正如我用 high_resolution_clock 測量的那樣,幾乎是 0,此時沒有睡眠。 當有睡眠時,它會變得更大。 我不希望浪費 cpu 資源並想做這樣的事情:當接收線程獲取任務時,它通知一個處理線程,它執行 wait_for,當處理任務完成時,它以相同的方式通知響應線程。 因此,我認為我會花費更少的時間,並且不會浪費 cpu 資源。 我有一些問題:

  1. 這會按照我認為的方式工作嗎,唯一的區別是在通知時醒來?
  2. 為此,我必須創建 2 個條件變量:第一個用於接收線程和所有處理,第二個用於所有處理和響應? 處理線程中的互斥鎖必須對所有線程都是通用的還是唯一的?
  3. 我可以在 if 分支中創建 unique_lock(mutex) 和 wait_for() 而不是 sleep_for 嗎?
  4. 如果某些處理線程很忙,是否可能 notify_one() 可以嘗試喚醒其中一個,但不能喚醒空閑線程? 我需要使用 notify_all()?
  5. 通知是否有可能不會喚醒任何線程? 如果有,是不是概率大?

這會按照我認為的方式工作嗎,唯一的區別是在通知時醒來?

是的,假設你做對了。

為此,我必須創建 2 個條件變量:第一個用於接收線程和所有處理,第二個用於所有處理和響應? 處理線程中的互斥鎖必須對所有線程都是通用的還是唯一的?

您可以使用單個互斥鎖和單個條件變量,但這會使其更加復雜。 我建議使用單個互斥鎖,但線程可能要等待的每個條件都有一個條件變量。

我可以在 if 分支中創建 unique_lock(mutex) 和 wait_for() 而不是 sleep_for 嗎?

絕對不。 您需要在檢查隊列是否為空時持有互斥鎖並繼續持有它,直到您調用wait_for 否則,您將破壞條件變量的整個邏輯。 與條件變量關聯的互斥鎖必須保護線程將要等待的條件,在這種情況下是隊列非空。

如果某些處理線程很忙,是否可能 notify_one() 可以嘗試喚醒其中一個,但不能喚醒空閑線程? 我需要使用 notify_all()?

我不知道您所說的“免費線程”是什么意思。 作為一般規則,如果無法在無法處理條件的條件變量上阻塞線程,則可以使用notify_one 如果可能需要喚醒多個線程,或者可能會在條件變量上阻塞多個線程並且可能喚醒“錯誤線程”,即可能至少有一個線程,則應使用notify_all不能做任何需要做的事情的線程。

通知是否有可能不會喚醒任何線程? 如果有,是不是概率大?

當然,這很有可能。 但這意味着在這種情況下沒有線程被阻塞。 在這種情況下,沒有線程可以阻塞條件,因為線程必須在wait之前檢查條件,並且它們在持有互斥鎖的同時進行。 提供這種原子的“解鎖和等待”語義是條件變量的全部目的。

您擁有的機制稱為輪詢。 線程反復檢查(輪詢)是否有可用數據。 正如你提到的,它有浪費時間的缺點。 (但這很簡單)。 你提到的你想使用的東西叫做阻塞機制。 這將取消調度線程,直到工作變得可用。

1)是的(雖然我不知道你在想什么)

2)a)是的,2個條件變量是一種方法。 b) 普通互斥鎖是最好的

3)您可能會將它們放在pop中,並且調用pop可能會阻塞。

4) 不會。 notify_one只會喚醒當前正在等待的線程調用wait 此外,如果有多個在等待,則不一定能保證哪個會收到通知。 (操作系統/庫依賴)

5) 否。如果有 1+ 個線程在等待, notify_one保證喚醒一個。 但是如果沒有線程在等待,則通知被消耗(並且沒有效果)。 請注意,在某些邊緣條件下, notify_one實際上可能會喚醒多個。 此外,線程可能會在沒有人調用notify_one (“虛假喚醒”)的情況下從wait中喚醒。 完全可能發生的事實意味着您總是需要對其進行額外的檢查。

這被稱為生產者/消費者問題。

一般來說,您對條件變量的考慮是正確的。 我的建議更多地與此類功能的設計和可重用性相關。 主要思想是實現ThreadPool模式,它有帶有工作線程數的構造函數,方法submitTask,shutdown,join。 有了這樣的 class,您將使用 2 個池實例:一個用於處理多線程,第二個(由您選擇單線程)用於結果發送。 池由任務阻塞隊列和工作線程數組組成,每個執行相同的“彈出任務並運行”循環。阻塞隊列封裝了互斥體和cond_var。 Task 是通用函子。 這也將您的設計帶入了面向任務的方法,這在您的應用程序的未來有很多優勢。 如果您喜歡這個想法,歡迎您提出有關實施細節的更多問題。 最好的問候,丹尼爾

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM