簡體   English   中英

為什么它不起作用? 簡單的多線程示例

[英]Why it doesn't work? Simple multithreading example

你能幫我理解為什么這段代碼會凍結程序嗎?

#include <iostream>
#include <thread>
#include <mutex>

using namespace std;

int i = 0;

mutex mx;

void foo() {
    while(1) {
        lock_guard<mutex> locker(mx);
        i++;
        if(i == 5000) {
            break;
        }
    }
}

void boo() {
    while(1) {
        if(i == 100) {
            lock_guard<mutex> locker(mx);
            i = 5000;
            break;
         }
    }
}

int main(int argc, char *argv[])
{
    thread th1(foo);
    thread th2(boo);

    th1.join();
    th2.join();

    return 0;
}

為什么我會有這樣的結果? 如何更改代碼以使其正確? 你能告訴我你的想法嗎? 謝謝。

即使boo先開始運行,它也可能永遠不會看到i==100

如果您只有一個 CPU,那么當i==100時,CPU 不太可能從foo切換到boo

如果你有多個 CPU,那么i==100可能永遠不會進入foo's緩存,因為i不是易失性的,並且互斥鎖在讀取之間沒有鎖定。

實際上,編譯器在第一次之后甚至不必讀取i ,因為沒有 memory 障礙。 它可以假設值沒有改變。

即使你要解決這個問題,在boo注意到之前i可能會增加超過 100 的明顯可能性仍然存在。 看起來您希望這兩個線程“輪流”,但事實並非如此。

您的解決方案存在一些並發問題:

  1. 您必須始終如一地鎖定互斥體。 所有i的訪問都必須受到互斥量的保護,因此在if (i == 100) {行也是如此。 在沒有同步的情況下,編譯器可以自由地優化線程,就好像它在孤立地運行一樣,並假設i永遠不會改變。

  2. 不能保證boo會在foo之前開始。 如果它在之后開始, i將已經增加到遠高於100

  3. 不能保證互斥鎖定是公平的。 競爭同一個互斥量的兩個線程不會以交錯方式運行。 這意味着foo可能會在boo有機會運行之前多次增加i ,因此boo看到的i的值可能很容易從0跳到1000 ,跳過所需的100

  4. 孤立地, foo將“逃跑”,將i增加到遠遠超過5000 應該有一些退出或重新啟動條件。

如何更改代碼以使其正確?

添加一些同步以強制交錯處理。 例如,使用condition_variable s 在線程之間發出信號:

int i = 0;

mutex mx;
condition_variable updated_cond;
bool updated = false;
condition_variable consumed_cond;
bool consumed = true;

void foo() {
    while (1) {
        unique_lock<mutex> locker(mx);
        consumed_cond.wait(locker, [] { return consumed; });
        consumed = false;
        if (i == 5000) {
            break;
        }
        std::cout << "foo: i = " << i << "+1\n";
        i++;
        updated = true;
        updated_cond.notify_one();
    }
    std::cout << "foo exiting\n";
}

void boo() {
    for (bool exit = false; !exit; ) {
        unique_lock<mutex> locker(mx);
        updated_cond.wait(locker, [] { return updated; });
        updated = false;
        std::cout << "boo: i = " << i << "\n";
        if (i == 100) {
            i = 5000;
            exit = true;
        }
        consumed = true;
        consumed_cond.notify_one();
    }
    std::cout << "boo exiting\n";
}

該程序的行為是未定義的,因此對其所做的推理是徒勞的。 問題是boo讀取i的值, foo都讀取和寫入i的值,但是booif (i == 100)i的讀取相對於發生在foo中的寫入是無序的。 那是一場數據競賽,程序的行為是未定義的。 當然,您可以猜測可能會發生什么,但如果您希望代碼正確運行,則必須確保不存在數據競爭。 這意味着使用某種形式的同步:要么將boo中的鎖移到if之前,要么擺脫互斥並將i的類型更改為std::atomic<int>

暫無
暫無

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

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