如何使用std :: atomic实现可重用的线程屏障 [英] How to implement a re-usable thread barrier with std::atomic

查看:173
本文介绍了如何使用std :: atomic实现可重用的线程屏障的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有N个线程执行各种任务,这些线程必须定期与线程屏障同步,如下图所示3线程和8个任务。 ||

I have N threads performing various task and these threads must be regularly synchronized with a thread barrier as illustrated below with 3 thread and 8 tasks. The || indicates the temporal barrier, all threads have to wait until the completion of 8 tasks before starting again.

Thread#1  |----task1--|---task6---|---wait-----||-taskB--|          ...
Thread#2  |--task2--|---task5--|-------taskE---||----taskA--|       ...
Thread#3  |-task3-|---task4--|-taskG--|--wait--||-taskC-|---taskD   ...

我找不到一个可行的解决方案,想到了一本小册子Semaphores http://greenteapress.com/semaphores/index.html 是鼓舞人心的。我想出了一个解决方案使用std ::原子如下所示似乎使用三个std ::原子。
我担心我的代码分解在角落案例,因此引用的动词。所以,你可以共享关于验证这样的代码的建议吗?你有一个更简单的傻瓜验证码可用吗?

I couldn’t find a workable solution, thought the little book of Semaphores http://greenteapress.com/semaphores/index.html was inspiring. I came up with a solution using std::atomic shown below which "seems" to be working using three std::atomic. I am worried about my code breaking down on corner cases hence the quoted verb. So can you share advise on verification of such code? Do you have a simpler fool proof code available?

std::atomic<int> barrier1(0);
std::atomic<int> barrier2(0);
std::atomic<int> barrier3(0);

void my_thread()
{

  while(1) {
    // pop task from queue
    ...
    // and execute task 
    switch(task.id()) {
      case TaskID::Barrier:
        barrier2.store(0);
        barrier1++;
        while (barrier1.load() != NUM_THREAD) {
          std::this_thread::yield();
        }
        barrier3.store(0);
        barrier2++;
        while (barrier2.load() != NUM_THREAD) {
          std::this_thread::yield();
        }
        barrier1.store(0);
        barrier3++;
        while (barrier3.load() != NUM_THREAD) {
          std::this_thread::yield();
        }
       break;
     case TaskID::Task1:
       ...
     }
   }
}


推荐答案

对于原子变量,使用它们中的三个作为一个障碍只是过度杀死,只会使问题复杂化。你知道线程的数量,所以你可以简单地自动增加一个计数器每次线程进入屏障,然后旋转,直到计数器变得大于或等于N.这样的东西:

With atomic variables, using three of them for a barrier is simply overkill that only serves to complicate the issue. You know the number of threads, so you can simply atomically increment a single counter every time a thread enters the barrier, and then spin until the counter becomes greater or equal to N. Something like this:

void barrier(int N) {
    static std::atomic<unsigned int> gCounter = 0;
    gCounter++;
    while((int)(gCounter - N) < 0) std::this_thread::yield();
}

如果你没有比CPU核心更多的线程,时间,您可能想要删除对 std :: this_thread :: yield()的调用。这个电话可能是真的很贵(超过一个微秒,我下注,但我没有测量)。

If you don't have more threads than CPU cores and a short expected waiting time, you might want to remove the call to std::this_thread::yield(). This call is likely to be really expensive (more than a microsecond, I'd wager, but I haven't measured it). Depending on the size of your tasks, this may be significant.

如果你想重复一些障碍,只需增加 N 随你走:

If you want to do repeated barriers, just increment the N as you go:

unsigned int lastBarrier = 0;
while(1) {
    switch(task.id()) {
        case TaskID::Barrier:
            barrier(lastBarrier += processCount);
            break;
    }
}

这篇关于如何使用std :: atomic实现可重用的线程屏障的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆