用于线程同步的std :: atomic vs static变量

Eve*_*one 2 c++ multithreading synchronization atomic

我有一堆线程正在处理多个数据项.线程必须按照我将数据传递给线程的相同顺序输出结果.那是:

Thread #1: give data - start processing
Thread #2: give data - start processing
Thread #3: give data - start processing
...
Thread #n: give data - start processing
Run Code Online (Sandbox Code Playgroud)

应该以与传递给线程的数据相同的顺序检索结果,而不管哪个线程首先完成处理.即:

Thread #1: put data 
Thread #2: put data
...
Run Code Online (Sandbox Code Playgroud)

为了区分线程并管理它们,我给每个人一个ID (0,1,2,...,n).我正在使用ID为每个线程分配数据,以便它可以处理它.

for(int i=0; i<thread_count; i++)
    give_data(i); // i is id and the function knows where to get data from
Run Code Online (Sandbox Code Playgroud)

我希望线程共享一个令牌,该令牌决定了预期产生结果的线程.所有线体都是相同的,主体看起来像:

while(true){
    auto data = get_data();
    result = process_data(data);
    while(token != this_id) spin;
    put_data(result); // this is a synchronized call 
    update_token(token);
}
Run Code Online (Sandbox Code Playgroud)

我的问题来自于token.我首先尝试了一个普通的引用(int & token),它显然无法工作(我没想到它).无论如何,我使用了一个静态变量,并且线程并不总是得到最新的.我很惊讶地看到一个主线支配一切.每当线程更新令牌时,它就会失去允许另一个线程放置其结果等等.但是,我有一个主线,好像令牌总是设置为自己的ID而不是更新.

如果我不得不猜测我会说这是一个缓存问题.但是,我不确定.

无论如何,我正在考虑使用std::atomic<int>我的令牌.会有用吗?如果没有,我还应该考虑做什么?什么是同步这些线程的更好方法?

额外:这感觉就像一个糟糕的设计,我不知道如何做得更好.任何建议将非常感谢.

Max*_*hof 6

无论如何,我使用了一个静态变量,并且线程并不总是得到最新的.我很惊讶地看到一个主线支配一切

是的,多个线程访问相同的非同步值,其中至少有一个写入它是数据争用,根据C++标准,这是未定义的行为.什么事情都可能发生.

我正在考虑使用std :: atomic作为我的令牌.会有用吗?

是.这可以防止令牌上的任何数据竞争.我没有在你的伪代码中看到任何其他直接问题,所以从这个角度来看它看起来不错.

这感觉就像一个糟糕的设计,我不知道如何做得更好.任何建议将非常感谢.

整个设计确实看起来有些奇怪,但如果有更简单的表达方式,它取决于您的线程库.例如,使用OpenMP,您可以在一次传递中执行此操作(背后的逻辑,give_data并且get_data太不清楚,以使其完整):

#pragma omp parallel
{
    int threadCount = omp_get_num_threads();
#pragma omp single
    for (int i = 0; i < threadCount; ++i)
        give_data(i);

#pragma omp ordered for ordered schedule(static)
    for (int i = 0; i < threadCount; ++i)
    {
        auto data = get_data();
        result = process_data(data);
#pragma omp ordered
        put_data(result); // this is a synchronized call 
    }
}
Run Code Online (Sandbox Code Playgroud)

ordered指令强制put_data调用以完全相同的顺序(逐个)执行,就像循环是串行的一样,而线程仍然可以并行执行先前的数据处理.

如果您真正想要做的就是使一个大的数据处理循环与有序写入并行,那么使用OpenMP实际上可能会更容易:

#pragma omp parallel for ordered schedule(static)
for (int i = 0; i < dataItemCount; ++i)
{
    auto data = get_data(i); // whatever this would entail
    auto result = process_data(data);
#pragma omp ordered
    put_data(result); // this is a synchronized call    
}
Run Code Online (Sandbox Code Playgroud)

看起来您不需要按顺序排列数据项,但如果您确实这样做,那么这种方法就不会那么容易,因为每个有序循环只能有一个有序的部分.