即使使用互斥锁,c ++ std :: thread上也会出现死锁

时间:2017-09-06 12:40:18

标签: c++ multithreading locking mutex deadlock

我试图更好地理解c ++线程。我实现了一个小例子,我想要四个线程来处理std :: vector中的索引区域。

当然我遇到了死锁,并开始使用互斥锁和锁。据我了解,一般的假设是所有变量都由线程共享,除非另有明确说明(thread_local)。如果一个线程改变了任何全局数据,那么首先锁定资源是明智的,对数据进行处理以避免数据竞争,然后再次解锁数据,以便其他线程可以使用它。

在我的例子中,std :: cout上的锁工作正常,线程创建正常,函数被称为正常,但程序仍然挂起,即使我在操作数据之前和之后实现了data_lock。 如果我注释掉数据操作并显示消息,它也可以正常工作。输出与运行不同,所以我不发布它。

我的感觉是我错过了一些我不知道的c ++线程的概念(之前我曾与MPI合作过)。

我的问题:

  1. 我缺少一个概念吗?我还需要知道/阅读什么?
  2. 除了使用互斥锁,锁和thread_local进行正确执行之外,还有哪些工具?
  3. 编译器指令:

    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;
    }
    

1 个答案:

答案 0 :(得分: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(以及许多其他),here

  

RAII保证资源可用于任何功能   可以访问该对象(资源可用性是一个类不变量,   消除冗余运行时测试)。它也保证了所有   资源在其控制对象的生命周期中释放   以收购的相反顺序结束。同样,如果资源   获取失败(构造函数以异常退出),全部   每个完全构建的成员和基地获得的资源   子对象以初始化的相反顺序释放。这个   利用核心语言功能(对象生命周期,范围退出,   初始化和堆栈展开的顺序)以消除资源   泄漏并保证异常安全。这种技术的另一个名称   在基本用例之后是范围限制资源管理(SBRM)   其中RAII对象的生命周期因范围退出而终止。