在没有互斥体的情况下在C ++ 11中实现共享整数计数器的最简单方法:

时间:2014-02-17 17:06:09

标签: c++ multithreading c++11 stdatomic

假设我们有以下代码来计算出现事件的次数:

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)最快的方法?,最好不使用互斥锁。感谢。

更新:使用注释来使用atomics,我得到了一个在英特尔编译器版本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 个答案:

答案 0 :(得分:16)

您可能需要查看atomic types。您可以在不需要锁/互斥锁的情况下访问它们。

答案 1 :(得分: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;
}

答案 2 :(得分:1)

您可以使用InterlockedIncrement功能。

MSDN在Synchronization Functions的标语下记录了许多以原子方式改变变量的函数 - 它们可能对您有用。

答案 3 :(得分:1)

这不仅仅是一种竞争条件,如果您的编译器决定,您可能根本没有在线程之间传递实际的i值。

显然atomic是好方法。互斥也是一种好方法,当你没有碰撞时,它们就像原子一样快。只有当他们真正需要使内核与sleepingready线程队列混合时,它们才会变慢。可以获得taxy的是,如果等待信号没有使用condition variable,在这种情况下,您可能必须等待ready线程的计划内核标记为running,这可以很长(30ms)。

atomics会让你获得最优,甚至可能比condition variable更容易维护,这要归功于不必关心spurious事件和notify_onenotify_all

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