在C++11中实现共享整数计数器而不使用互斥的最简单方法:

Easiest way to implement shared integer counter in C++11 without mutexes:

本文关键字:最简单 方法 实现 C++11 共享 整数 计数器      更新时间:2023-10-16

假设我们有以下代码,它统计某些事情发生的次数:

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";
}

您可能需要研究原子类型。您可以在不需要锁/互斥的情况下访问它们。

我们通过声明一个数组[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;
}

您可以使用InterlockedIncrement函数。

许多以原子方式变异变量的函数都在MSDN上以同步函数的名义进行了记录,它们可能对您有用。

这不仅仅是一个竞争条件,如果编译器决定的话,线程之间可能根本没有实际i值的通信。

显然CCD_ 2是一个好方法。互斥也是一个很好的方法,当你没有冲突时,它们就像原子一样快。只有当它们真正需要让内核处理sleepingready线程队列时,它们才会变慢。如果等待信号不使用condition variable,则可能需要等待ready线程为running的调度内核滴答声,这可能非常长(30ms)。

不过,原子论会让你获得最优性,甚至可能比condition variable更容易维护,因为不必关心spurious事件和notify_onenotify_all等。

如果您检查C++11生成的STL的shared_ptr基类,它们包含一个base_count或(base_shared_count或其他什么),它的工作方式与您的需要完全一样。如果愿意,您也可以检查新的boost::shared_count实现。