簡體   English   中英

if語句僅在調試cout行之后才通過(C中的多線程)

[英]If statement passes only when preceded by debug cout line (multi-threading in C)

我創建了此代碼,用於實時解決CPU密集型任務,並有可能在將來作為游戲引擎的基礎。 為此,我創建了一個系統,其中有一個整數數組,每個線程都會對其進行修改,以表示是否已完成當前任務。

使用四個以上的線程運行它時,會發生此問題。 當使用6個或更多線程時, “ if(threadone_private == threadcount)”會停止工作,除非我添加此調試行“ cout << threadone_private << endl;”。 在它之前。

我無法理解為什么此調試行對if條件函數是否按預期起作用有什么區別,也不能理解為什么在使用4個線程或更少線程的情況下,如果沒有該條件,調試行是否起作用。

對於此代碼,我使用:

#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <iostream>
#include <thread>
#include <atomic>
#include <vector>
#include <string>
#include <fstream>
#include <sstream>

using namespace std;

目前,此代碼僅以高達30億的異步步長迅速增長到60萬億。

以下是代碼的相關部分:

int thread_done[6] = { 0,0,0,0,0,0 };
atomic<long long int> testvar1 = 0;
atomic<long long int> testvar2 = 0;
atomic<long long int> testvar3 = 0;
atomic<long long int> testvar4 = 0;
atomic<long long int> testvar5 = 0;
atomic<long long int> testvar6 = 0;

void task1(long long int testvar, int thread_number)
{
    int continue_work = 1;
    for (; ; ) {
        while (continue_work == 1) {
            for (int i = 1; i < 3000000001; i++) {
                testvar++;
            }
            thread_done[thread_number] = 1;
            if (thread_number==0) {
                testvar1 = testvar;
            }
            if (thread_number == 1) {
                testvar2 = testvar;
            }
            if (thread_number == 2) {
                testvar3 = testvar;
            }
            if (thread_number == 3) {
                testvar4 = testvar;
            }
            if (thread_number == 4) {
                testvar5 = testvar;
            }
            if (thread_number == 5) {
                testvar6 = testvar;
            }
            continue_work = 0;
        }
        if (thread_done[thread_number] == 0) {
            continue_work = 1;
        }
    }
}

這是主線程的相關部分:

int main() {
    long long int testvar = 0;
    int threadcount = 6;
    int threadone_private = 0;
    thread thread_1(task1, testvar, 0);
    thread thread_2(task1, testvar, 1);
    thread thread_3(task1, testvar, 2);
    thread thread_4(task1, testvar, 3);
    thread thread_5(task1, testvar, 4);
    thread thread_6(task1, testvar, 5);
    for (; ; ) {
        if (threadcount == 0) {
            for (int i = 1; i < 3000001; i++) {
                testvar++;
            }
            cout << testvar << endl;
        }
        else {
            while (testvar < 60000000000000) {
                threadone_private = thread_done[0] + thread_done[1] + thread_done[2] + thread_done[3] + thread_done[4] + thread_done[5];
                cout << threadone_private << endl;
                if (threadone_private == threadcount) {
                    testvar = testvar1 + testvar2 + testvar3 + testvar4 + testvar5 + testvar6;
                    cout << testvar << endl;
                    thread_done[0] = 0;
                    thread_done[1] = 0;
                    thread_done[2] = 0;
                    thread_done[3] = 0;
                    thread_done[4] = 0;
                    thread_done[5] = 0;
                }
            }
        }
    }
}

我期望由於每個工作線程僅修改線程threadone_private數組中的一個int,並且由於主線程僅在所有工作線程都在等待之前才讀取它,因此if(threadone_private == threadcount)應該是防彈的……顯然我每當我更改此設置時,都會丟失一些會出錯的重要信息:

                threadone_private = thread_done[0] + thread_done[1] + thread_done[2] + thread_done[3] + thread_done[4] + thread_done[5];
                cout << threadone_private << endl;
                if (threadone_private == threadcount) {

對此:

threadone_private = thread_done[0] + thread_done[1] + thread_done[2] + thread_done[3] + thread_done[4] + thread_done[5];
//cout << threadone_private << endl;
                if (threadone_private == threadcount) {

免責聲明:並發代碼非常復雜,容易出錯,因此使用更高級別的抽象通常是一個好主意。 有很多細節很容易出錯而不會引起注意。 如果您不是專家,則應該非常仔細地考慮進行這樣的低級編程。 令人遺憾的是,C ++缺少良好的內置高級並發構造,但是那里有處理該問題的庫。

目前尚不清楚整個代碼對我有什么作用。 據我所知,代碼是否會停止完全取決於定時-即使您正確地進行了同步-這完全是不確定的。 您的線程可以以這樣的方式執行: thread_done永遠thread_done是真的。

但是除此之外,至少存在一個正確性問題:您正在讀寫int thread_done[6] = { 0,0,0,0,0,0 }; 沒有同步。 這是未定義的行為,因此編譯器可以執行所需的操作。

可能發生的情況是,編譯器認為它可以緩存threadone_private的值,因為線程從不對其進行寫操作,因此該值無法更改(合法地)。 std::cout的外部調用意味着無法確定該值在背后沒有變化,因此它必須在每次新迭代時讀取該值(而且std :: cout使用鎖,這會導致大多數實現中的同步,再次限制了編譯器的假設。

我在您的代碼中看不到任何std :: mutex,std :: condition_variable或std :: lock的變體。 如果沒有這些,那么執行多線程將永遠不會成功。 因為無論何時有多個線程修改相同的數據,您都需要確保在任何給定時間只有一個線程(包括您的主線程)可以訪問該數據。

編輯:我注意到您使用原子。 我對此沒有任何經驗,但是我知道使用互斥體可以可靠地工作。

因此,您需要使用互斥鎖來鎖定對數據的每次訪問(讀或寫),如下所示:

//somewhere
std::mutex myMutex;
std::condition_variable myCondition;
int workersDone = 0;

/* main thread */

createWorkerThread1();
createWorkerThread2();

{
    std::unique_lock<std::mutex> lock(myMutex); //waits until mutex is locked.
    while(workersDone != 2) {
        myCondition.wait(lock); //the mutex is unlocked while waiting
    }
    std::cout << "the data is ready now" << std::endl;
} //the lock is destroyed, unlocking the mutex

/* Worker thread */

while(true) {
    {
        std::unique_lock<std::mutex> lock(myMutex); //waits until mutex is locked

        if(read_or_modify_a_piece_of_shared_data() == DATA_FINISHED) {
            break; //lock leaves the scope, unlocks the mutex
        }
    }

    prepare_everything_for_the_next_piece_of_shared_data(); //DO NOT access data here
}

//data is processed
++workersDone;
myCondition.notify_one(); //no mutex here. This wakes up the waiting thread

我希望這會讓您對如何使用互斥量和條件變量獲得線程安全性有所了解。

免責聲明:100%偽代碼;)

暫無
暫無

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

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