如何在没有轮询的情况下使线程等待?

时间:2016-11-03 17:56:00

标签: c++ multithreading

我对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 blanket如何通知所有等待线程他们现在可以自由地读取更多数据?

编辑: 以防任何人需要更多细节

1)读者读取一些文件 2)thread_buckets是uint16向量的向量 3)threadflags是一个int向量

他们都已适当调整大小

2 个答案:

答案 0 :(得分: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

答案 1 :(得分:1)

看起来有一些问题。首先,你不需要循环中的条件:

while (thread_flags[thread_num] != THREAD_RUNNING);

将独立工作。只要该条件为假,循环就会退出。

如果您想要做的就是尽可能快地检查thread_flags,只需在循环中输入:

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

这将导致线程产生CPU,以便在线程等待其状态发生变化时可以执行其他操作。这将使轮询的开销接近可忽略不计。您可以尝试睡眠持续时间以找到一个好的值。 100毫秒可能是偏长的。

根据导致线程状态发生变化的原因,您可以让线程轮询直接调用条件/值(仍处于休眠状态)并且根本不打扰状态。

这里有很多选择。如果你查阅读者线程,你可能会找到你想要的东西;有一个单独的读者线程是很常见的。