簡體   English   中英

C ++ std :: thread死鎖,即使具有互斥鎖

[英]deadlock on c++ std::thread even with mutex lock

我試圖更好地理解c ++線程。 我實現了一個小示例,其中我希望四個線程​​在std :: vector的索引區域上工作。

當然,我遇到了死鎖,並開始研究互斥鎖和鎖的使用。 據我了解,一般的假設是所有變量都由線程共享,除非另有明確說明(thread_local)。 如果線程更改了任何全局數據,明智的做法是先鎖定資源,對數據進行處理以避免數據爭用,然后再次解鎖數據,以便其他線程可以使用它。

在我的示例中,對std :: cout的鎖定可以正常工作,可以很好地創建線程,可以調用該函數,但是即使我在處理數據前后都實現了data_lock,程序仍然掛起。 如果我注釋掉數據操作並顯示一條消息,它也可以正常工作。 輸出與運行之間是不同的,所以我避免發布它。

我的感覺是,我錯過了一些我不知道的c ++線程概念 (我之前曾使用過MPI)。

我的問題:

  1. 有沒有我想念的概念? 我還需要了解/閱讀什么?
  2. 除了使用互斥鎖,鎖和thread_local正確執行以外,還有哪些工具?

編譯器指令:

g ++ -std = c ++ 1y -O0 -g3 -Wall -c -fmessage-length = 0 -pthread -MMD -MP -MF -src / main.d“ -MT” src / main.d“ -o” src /main.o“” ../src/main.cpp“

碼:

#include <iostream>
#include <thread>
#include <vector>
#include <mutex>
using namespace std;

std::mutex data_lock;
std::mutex cout_lock;

void output(std::string message){
    cout_lock.lock();
    cout << message << endl;
    cout_lock.unlock();
}

void work(std::vector<double>& data, const int s_ind, const int e_ind)     {
    thread_local int i = 0;
    for (i = s_ind; i <= e_ind; i++) {
        data_lock.lock();
        data[i] = 1.0;
        data_lock.unlock();
        //msg("work");
    }
}

int main() {
    const int size = 1000;
    const int cpus = 4;
    const int chunksize = size / cpus;

    //create Data vector
    std::vector<double> dat { (size) };

    //thread vector
    std::vector<std::thread> threads;

    //create and start threads with proper ranges (ranges tested)
    for (int cpu = 0; cpu < cpus; cpu++) {
        threads.push_back(std::thread(work, ref(dat), (cpu * chunksize),(((cpu + 1) * chunksize) - 1)));
        output("thread created");
    }

    //delete threads
    for (int cpu = 0; cpu < cpus; cpu++) {
        threads[cpu].join();
        output("thread joined");
    }
return 0;
}

我知道有人建議在ctor中使用{} ,這樣可以消除隱式函數聲明的問題。 但是,如果類具有構造函數重載且將std::initializer_list<T>作為參數,而std :: vector則有問題,則會遇到問題。 所以這行:

std::vector<double> dat { (size) };

創建一個具有一個元素size向量。

當您聲明陷入僵局並必須使用互斥鎖時,這幾乎是不可能的。 您的程序很可能掛起,但這不認為是死鎖。 死鎖是指您的線程互相阻塞,試圖以錯誤的順序鎖定互斥鎖(或類似情況)。

注意:這不是固定的,但是您應該使用RAII進行互斥鎖,標准庫為此提供了工具: std :: lock_guardstd :: unique_lockstd :: scoped_lock(如果您使用的是c ++ 17)。 究其原因,為什么要RAII在這種情況下(以及其他許多)被用來描述在這里

RAII保證資源可用於可能訪問該對象的任何函數(資源可用性是類不變的,從而消除了冗余的運行時測試)。 它還可以確保在其控制對象的生命周期結束時以獲取的相反順序釋放所有資源。 同樣,如果資源獲取失敗(構造函數異常退出),則每個完全構造的成員和基礎子對象獲取的所有資源將以初始化的相反順序釋放。 這利用了核心語言功能(對象生存期,范圍退出,初始化順序和堆棧展開)來消除資源泄漏並確保異常安全。 這種技術的另一個名稱是作用域綁定資源管理(SBRM),在基本用例之后,RAII對象的生存期由於作用域退出而結束。

暫無
暫無

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

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