即使使用MUTEX锁,C STD上的僵局也是如此



我尝试更好地了解C 线程。我实现了一个小例子,我希望四个线程在std :: vector中的索引区域上工作。

当然,我遇到了僵局,并阅读了静音和锁的使用。据我了解,一般的假设是所有变量均由线程共享,除非另有明确说明(thread_local)。如果线程改变了任何全局数据,那么明智的明智是锁定追索权,请在数据上进行工作以避免数据竞赛,然后再次解锁数据,以便其他线程可以使用它。

在我的示例中,std :: cout上的锁可以正常工作,线程良好,函数称为良好,但是即使我在操纵数据之前和之后我实现了data_lock,该程序仍然悬挂。如果我评论数据操纵并显示消息,也可以正常工作。输出与运行不同,所以我避免发布它。

我的感觉是我想念我不知道的C 线程的一些概念(我以前与MPI合作)。

我的问题:

  1. 我缺少一个概念吗?我还需要知道/阅读什么?
  2. 除了使用静音,锁和线程_local以适当执行以外,还有什么工具?

编译器指令:

g -std = c 1y -o0 -g3 -wall -c -fmessage -length = 0 -pthread -mmd -mmd -mmd -mmd -mmf" 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中使用{},它消除了Immplicit函数声明的问题。但是,如果类具有与std::initializer_list<T>的构造函数过载作为参数,并且std :: vector确实有构造函数过载。所以这条线:

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

用其中一个元素size创建向量。

作为您陷入僵局并必须使用静音的声明,这几乎是不可能的。您的程序很可能会挂起,但这并不是僵局。僵局是一种情况,当您的线程试图以错误的顺序锁定静音(或类似情况)。

注意:这不是修复,但是您应该将raii用于静音锁,标准库为此提供工具:std :: lock_guard,std :: simelod_lock或std :: scoped_lock如果您有C 17。原因,在这种情况下应使用RAII(以及许多其他)的原因

raii保证该资源可用于任何功能 可以访问对象(资源可用性是类不变的, 消除冗余运行时测试)。它还保证了所有人 当他们的控制对象的寿命时,资源将发布 结束,以收购的相反顺序。同样,如果资源 采集失败(构造函数退出,例外),所有 每个完整构建的成员和基础获得的资源 亚对象以初始化的相反顺序释放。这 利用核心语言功能(对象寿命,范围退出, 初始化和堆栈放松的顺序)以消除资源 泄漏并保证例外安全。该技术的另一个名称 是基本用例之后的范围结合资源管理(SBRM) RAII对象的寿命由于范围出口而结束。

最新更新