2016-11-03 124 views
1

我有关于C++多线程的问题。我有一个场景如下如何在不进行轮询的情况下等待线程?

void ThreadedRead(int32_t thread_num, BinReader reader) { 
    while (!reader.endOfData) { 
     thread_buckets[thread_num].clear(); 
     thread_buckets[thread_num] = reader.readnextbatch() 
     thread_flags[thread_num] = THREAD_WAITING; 
     while (thread_flags[thread_num] != THREAD_RUNNING) { 
      // wait until awakened 
      if (thread_flags[thread_num] != THREAD_RUNNING) { 
       //go back to sleep 
      } 
     } 
    } 
    thread_flags[thread_num] = THREAD_FINISHED; 
} 

没有上述代码段写入或访问线程之间共享的内存。每个线程都分配了一个thread_num和一个可用于读取数据的唯一读取器对象。

我想让主线程能够通知处于THREAD_WAITING状态的线程,他的状态已经改回到THREAD_RUNNING,他需要做一些工作。我不想让他继续投票他的状态。

我了解条件变量和互斥锁可以帮助我。但我不知道如何使用它们,因为我不想获取或需要锁定。 mainthread毯子如何通知所有等待的线程,他们现在可以自由阅读更多数据?

编辑: 万一有人需要更多的细节

1)阅读器读取一些文件 2)thread_buckets是UINT16 3向量的向量)threadflags是INT矢量

他们都已适当调整大小

+2

只需使用一个条件变量,需要您的简历锁,因为调用cv.wait以外的情况下可能发生的数据竞争( )或cv.notify()。 –

+0

但没有数据以共享方式被访问?每个线程都有自己的数据成员,它操纵。我希望他们以非互斥的方式运行。我只是希望他们等到主线程说他们可以继续前进 –

回答

1

看起来像有几个问题。一方面,您不需要循环内的条件:

while (thread_flags[thread_num] != THREAD_RUNNING); 

将自行工作。只要该条件为假,循环将退出。

如果你想要做的就是避免尽快检查thread_flags,只是把循环产率:

while (thread_flags[thread_num] != THREAD_RUNNING) yield(100); 

这将导致线程让出CPU,以便它可以做其他事情而线程等待其状态改变。这将使投票的开销接近可忽略不计。你可以尝试睡眠时间来找到一个很好的价值。 100ms可能是长线。

根据导致线程状态改变的原因,你可以让线程直接轮询该条件/值(在睡眠状态下)并且根本不打扰状态。

这里有很多选项。如果你查看读者线程,你可能会找到你想要的;有一个单独的阅读器线程是非常普遍的。

+0

,但是有问题的任务可以比1000ms更快地完成我只是想等到任务完成时才需要:( –

+0

'sleep '不是一个好的产品代码 – slawekwin

+0

同意这两个计数我更新了我的答案,使用100ms代替(仍然很高但可能更接近你想要的)并使用yield()作为通用的线程产生函数来使它该线程在等待时不应占用CPU资源。 – Andrew

3

我意识到你写了你想避免条件变量和锁。另一方面,你提到这是因为你不确定如何使用它们。请考虑下面的例子把工作无投票完成:

与条件变量的诀窍是,一个单一的condition_variable对象与单一mutex对象一起会做管理,你包括在该unique_lock对象的处理工作者线程。由于您将问题标记为C++,因此我假设您正在讨论C++ 11(或更高版本)多线程(我猜C-pthreads可能会以类似的方式工作)。您的代码可能如下:

// compile for C++11 or higher 

#include <thread> 
#include <condition_variable> 
#include <mutex> 

// objects visible to both master and workers: 
std::condition_variable cvr; 
std::mutex    mtx; 

void ThreadedRead(int32_t thread_num, BinReader reader) { 
    while (!reader.endOfData) { 
     thread_buckets[thread_num].clear(); 
     thread_buckets[thread_num] = reader.readnextbatch() 

     std::unique_lock<std::mutex> myLock(mtx); 
     // This lock will be managed by the condition variable! 

     thread_flags[thread_num] = THREAD_WAITING; 
     while (thread_flags[thread_num] == THREAD_WAITING) { 
      cvr.wait(myLock); 
     // ...must be in a loop as shown because of potential spurious wake-ups 
     } 
    } 
    thread_flags[thread_num] = THREAD_FINISHED; 
} 

(重新)从主线程激活工人:

{ // block... 
// step 1: usually make sure that there is no worker still preparing itself at the moment 
    std::unique_lock<std::mutex> someLock(mtx); 
    // (in your case this would not cover workers currently busy with reader.readnextbatch(), 
    // these would be not re-started this time...) 

// step 2: set all worker threads that should work now to THREAD_RUNNING 
    for (...looping over the worker's flags...) { 
    if (...corresponding worker should run now...) { 
     flag = THREAD_RUNNING; 
    } 
    } 

// step 3: signalize the workers to run now 
    cvr.notify_all(); 

} // ...block, releasing someLock 

注意:

  • 如果你只是想触发所有睡眠你应该用一个标志来控制它们,而不是一个标志的容器。
  • 如果您想要触发单个睡眠工作人员,但是哪一个人考虑.notify_one()成员函数而不是.notify_all()并不重要。还要注意,在这种情况下,单个互斥/条件变量对就足够了。
  • 这些标志最好放在atomic对象中,例如全局的std::atomic<int>或可能为了更好地控制std::vector<std::atomic<int>>
  • 一个很好的介绍std::condition_variable这也激发了建议的解决方案给出的:cplusplus website
相关问题