通知线程退出

时间:2013-09-24 08:45:30

标签: c++ multithreading c++11 threadpool

这是一个简单的(可能是天真的)线程池实现。

我想知道,如果采用下面的方法,有一种很好的方法可以主动通知线程池线程退出,而不必在每次超时后检查布尔值mStopped

我知道有更好的方法来实现线程池 - 但我仍然有兴趣看看下面的代码是否可以在不从根本上改变的情况下得到改进。

我很高兴听到一般建议......不一定能修复下面的代码......问题实际上是关于线程上的信令/等待,线程池只是为了给出一些上下文。

我正在使用gcc 4.4.6(因此下面的一些语法有点过时),代码编译为g++ --std=c++0x main.cc -pthread

#include <vector>
#include <mutex>
#include <thread>
#include <deque>
#include <condition_variable>
#include <functional>

using namespace std;

struct ThreadPool;

struct Worker {
  ThreadPool& mThreadPool;
  Worker(ThreadPool& threadPool) : mThreadPool(threadPool) {}
  void operator()();
};

struct ThreadPool {
  vector<thread> mWorkers;
  deque<function<void()>> mTasks;
  bool mStopped; // not atomic
  mutex mMutex;
  condition_variable mCond;

  ThreadPool() : mStopped(false) {
    for (size_t i = 0; i < 5; i++)
      mWorkers.push_back(thread(Worker(*this)));
  }

  ~ThreadPool() { stop();  }

  void stop() {
    if (!mStopped) {
      mStopped = true;
      for (auto it = mWorkers.begin(); it != mWorkers.end(); ++it)
        if (it->joinable()) it->join();
    }
  }

  bool canBreakFromWait() const { return !mTasks.empty(); }

  void enqueue(function<void()> f) {
    if (mStopped) return;
    unique_lock<mutex> lck(mMutex);
    mTasks.push_back(f);
    mCond.notify_one();
  }
};

void Worker::operator()() {
  while (true) {
    unique_lock<mutex> lck(mThreadPool.mMutex);
    mThreadPool.mCond.wait_for(lck, chrono::seconds(1), bind(&ThreadPool::canBreakFromWait, &mThreadPool));
    if (mThreadPool.mStopped) break;
    auto task = mThreadPool.mTasks.front();
    mThreadPool.mTasks.pop_front();
    lck.unlock();
    task();
  }
}

void doWork(int data) {}

int main() {
  ThreadPool threadPool;
  for (auto i = 0; i < 50; i++)
    threadPool.enqueue(bind(doWork, i));
  threadPool.stop();
}

1 个答案:

答案 0 :(得分:2)

两条评论。

首先是因为mStopped在一个线程中被修改, 并且在其他线程中访问,必须对它进行所有访问 同步。目前,您的程序尚未定义 行为。这是否会成为实践中的问题,我不这样做 知道;先验,编译器可以在stop中推断出没有其他的 线程访问变量,并且不访问它 函数返回后调用stop的线程,等等 压制作业。

第二个是在我有工作清单的情况下,我已经 通常发现创建一个特殊的工作最简单 终止线程。标准线程似乎没有 一个thread_exit函数,但这可以很容易地模拟 要么让作业返回布尔值,要么使用例外。 然后stop函数锁定互斥锁,清空队列,然后 将其中一个特殊终止作业插入队列 对于每个线程。