C ++线程:尽管没有比赛,共享内存仍未更新

时间:2014-06-26 20:01:59

标签: c++ multithreading c++11 benchmarking stdthread

我正在尝试使用C ++标准线程。我写了一个小基准来测试性能开销和整体吞吐量。它在一个或多个线程中运行10亿次迭代循环的原理,不时地进行小的暂停。

在第一个版本中,我在共享内存中使用了计数器(即正常变量)。我推测了以下输出:

Sequential      1e+009 loops    4703 ms 212630 loops/ms
2 thrds:t1      1e+009 loops    4734 ms 211238 loops/ms
2 thrds:t2      1e+009 loops    4734 ms 211238 loops/ms
2 thrds:tt      2e+009 loops    4734 ms 422476 loops/ms
manythrd tn     1e+009 loops    7094 ms 140964 loops/ms
...  
manythrd tt     6e+009 loops    7094 ms 845785 loops/ms

不幸的是,显示屏显示了一些计数器,好像它们没有被初始化!

我可以通过将每个计数器的结束值存储在atomic<>中以便以后显示来解决问题。但是 我不明白为什么基于简单共享内存的版本无法正常工作 :每个线程都使用自己的计数器,因此没有竞争条件。即使显示线程仅在计数线程完成后才访问计数器。使用volatile也无济于事。

有人能解释我这种奇怪的行为(好像记忆没有更新)并告诉我是否错过了什么?

这里是共享变量:

const int maxthread = 6;
atomic<bool> other_finished = false;
atomic<long> acounter[maxthread];

这里是线程函数的代码:

void foo(long& count, int ic, long maxcount)   
{
    count = 0;  
    while (count < maxcount) {
        count++;
        if (count % 10000000 == 0)
            this_thread::sleep_for(chrono::microseconds(1));
    }
    other_finished = true;      // atomic: announce work is finished
    acounter[ic] = count;       // atomic: share result 
}

这里有一个关于我如何调用线程基准测试的例子:

mytimer.on();                 // second run, two threadeds
thread t1(foo, counter[0], 0, maxcount);  // additional thread
foo(counter[1], 1, maxcount);         // main thread
t1.join();                    // wait end of additional thread
perf = mytimer.off();     
display_perf("2 thrds:t1", counter[0], perf);  // non atomic version of code
display_perf("2 thrds:t2", counter[1], perf);
display_perf("2 thrds:tt", counter[0] + counter[1], perf);

1 个答案:

答案 0 :(得分:3)

以下是重现问题的简化版本:

void deep_thought(int& value) { value = 6 * 9; }

int main()
{
    int answer = 42;
    std::thread{deep_thought, answer).join();
    return answer; // 42
}

answer的引用传递给worker函数,并将6 * 9分配给引用,从而分配给answer。但是,std::thread的构造函数生成answer的副本,并将对副本的引用传递给worker函数,并且主线程中的变量answer永远不会更改。

GCC-4.9和Clang-3.5都拒绝上述代码,因为无法使用左值引用调用worker函数。您可以通过使用std::ref传递变量来解决问题:

    std::thread{deep_thought, std::ref(answer)}.join();