14

假设我们有以下代码来计算某事发生的次数:

int i=0;
void f() {
   // do stuff  . . .
   if(something_happens) ++i;
}

int main() {
    std::vector<std::thread> threads;
    for(int j = 0; j< std::thread::hardware_concurrency(); ++j) {
        threads.push_back(std::thread(f));
    }

    std::for_each(threads.begin(), threads.end(), std::mem_fn(&std::thread_join));
    std::cout << "i = " << i << '\n';
}

就目前而言,i 上有一个明确的竞争条件。使用 C++11,(1) 消除这种竞争条件的最简单方法是什么,以及 (2) 最快的方法是什么?最好不使用互斥锁。谢谢。

更新:使用注释来使用原子,我得到了一个在英特尔编译器版本 13 下编译的工作程序:

#include <iostream>
#include <thread>
#include <vector>
#include <atomic>
#include <algorithm>

std::atomic<unsigned long long> i = 0;

void f(int j) {
    if(j%2==0) {
        ++i;
    }  
}

int main() {
    std::cout << "Atomic i = " << i << "\n";
    int numThreads = 8; //std::thread::hardware_concurrency() not yet implemented by Intel
    std::vector<std::thread> threads;
    for(int k=0; k< numThreads; ++k) {
        threads.push_back(std::thread(f, k));
    }

    std::for_each(threads.begin(), threads.end(), std::mem_fn(&std::thread::join));
        std::cout << "Atomic i = " << i << "\n";
    }
4

4 回答 4

16

您可能想研究atomic types。您无需锁定/互斥即可访问它们。

于 2014-02-17T17:10:08.373 回答
3

我们通过声明一个数组 [nThreads] 解决了一个类似的问题,然后我们给每个线程一个从 0 到 n 的 id,然后线程可以安全地写入数组中的位置。然后你可以对数组求和以获得总和。但是,这仅在您不需要在所有线程都死之前对数组求和时才有用。

为了更高效,我们在每个线程上都有一个本地计数器,然后我们在线程死亡之前将其附加到数组中。

示例(伪代码:)

counter[nThreads];

thread(int id)
{
    // Do stuff
    if(something happened)
       counter[id]++;   
}

或者

counter[nThreads];

thread(int id)
{
    int localcounter = 0;
    //Do stuff
    if(something happened)
       localcounter++;   

    //Thread is about to die
    counter[id] = localcounter;
}
于 2014-02-17T17:22:04.670 回答
1

这不仅仅是一个竞争条件,i如果您的编译器决定这样做,您可能根本无法在线程之间进行实际值的通信。

显然atomic是好办法。互斥锁也是一个好方法,当你没有碰撞时,它们和原子一样快。只有当他们真正需要让内核摆弄线程队列时sleeping,它们才会变慢。ready如果等待信号不使用 a ,可能会变得很麻烦,condition variable在这种情况下,您可能必须等待调度内核滴答声才能让您的ready线程成为running,这可能很长(30 毫秒)。

atomics 会为您带来最优性,并且可能比condition variables 更容易维护,这要归功于不必关心spurious事件和notify_one对抗notify_all等。

如果您检查 C++11 制造的 STL 的 shared_ptr 基类,它们包含一个base_count或(base_shared_count 或其他东西),它完全符合您的需要。如果愿意,您还可以检查新的 boost::shared_count 实现。

于 2014-02-18T00:56:52.277 回答
1

您可以使用InterlockedIncrement函数。

许多以原子方式改变变量的函数都记录在 MSDN 上同步函数的旗帜下- 它们可能对您有用。

于 2014-02-17T17:08:05.873 回答