【问题标题】:c++ threading, duplicate/missing threadsc++线程,重复/丢失线程
【发布时间】:2019-03-11 17:39:32
【问题描述】:

我正在尝试编写一个程序,该程序可以同时从“仓库”中添加和删除项目。我有一个处理“仓库”操作的“监视器”类:

class Monitor
{
private:
    mutex m;
    condition_variable cv;
    vector<Storage> S;
    int counter = 0;
    bool busy = false;;
public:

    void add(Computer c, int index) {
        unique_lock <mutex> lock(m);
        if (busy)
            cout << "Thread " << index << ": waiting for !busy " << endl;
        cv.wait(lock, [&] { return !busy; });
        busy = true;
        cout << "Thread " << index << ": Request: add " << c.CPUFrequency << endl;

        for (int i = 0; i < counter; i++) {
            if (S[i].f == c.CPUFrequency) {
                S[i].n++;
                busy = false;   cv.notify_one();
                return;
            }
        }
        Storage s;
        s.f = c.CPUFrequency;
        s.n = 1;
        // put the new item in a sorted position
        S.push_back(s);
        counter++;
        busy = false; cv.notify_one();
    }
}

线程是这样创建的:

void doThreadStuff(vector<Computer> P, vector <Storage> R, Monitor &S)
{
    int Pcount = P.size();

    vector<thread> myThreads;
    myThreads.reserve(Pcount);

    for (atomic<size_t> i = 0; i < Pcount; i++)
    {
        int index = i;
        Computer c = P[index];
        myThreads.emplace_back([&] { S.add(c, index); });
    }

    for (size_t i = 0; i < Pcount; i++)
    {
        myThreads[i].join();
    }
// printing results
}

运行程序产生了以下结果:

我对比赛条件很熟悉,但对我来说这并不像。我的赌注是与参考相关的东西,因为在结果中我们可以看到,对于每个“缺失的线程”(线程 1、3、10、25),我都会得到“重复的线程”(线程 2、9、24、28)。

我尝试在函数和循环中创建局部变量,但没有任何改变。

我听说过线程共享内存区域,但我之前的工作应该产生类似的结果,所以我不认为这里是这种情况,但请随时证明我错了。

我正在使用 Visual Studio 2017

【问题讨论】:

    标签: c++ multithreading lambda


    【解决方案1】:

    这里你在一个循环中通过引用捕获局部变量,它们会在每一个回合中被销毁,导致未定义的行为:

    for (atomic<size_t> i = 0; i < Pcount; i++)
    {
        int index = i;
        Computer c = P[index];
        myThreads.emplace_back([&] { S.add(c, index); });
    }
    

    您应该按值捕获indexc

    myThreads.emplace_back([&S, index, c] { S.add(c, index); });
    

    【讨论】:

    • 对于一个看似漫长而神奇的问题,确实是一个简洁的好答案:P
    【解决方案2】:

    另一种方法是将Sic 作为参数传递,而不是通过定义以下非捕获 lambda、th_func 来捕获它们:

    auto th_func = [](Monitor &S, int index, Computer c){ S.add(c, index); };
    

    这样你必须显式包装必须通过引用传递给线程的可调用对象的参数std::reference_wrapper通过函数模板std::ref() .在你的情况下,只有S

    for (atomic<size_t> i = 0; i < Pcount; i++) {
        int index = i;
        Computer c = P[index];
        myThreads.emplace_back(th_func, std::ref(S), index, c);
    }
    

    未能使用std::reference_wrapper 包装必须通过引用传递的参数将导致编译时错误。也就是说,以下内容不会编译:

    myThreads.emplace_back(th_func, S, index, c); // <-- it should be std::ref(S)
    

    另见this question

    【讨论】:

      猜你喜欢
      • 2020-04-17
      • 2020-07-03
      • 2010-12-21
      • 1970-01-01
      • 1970-01-01
      • 2014-06-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-09
      相关资源
      最近更新 更多