2

这是线程池的一个简单(也可能是幼稚的)实现。

我想知道,鉴于下面的方法,是否有一种很好的方法可以主动通知线程池线程退出,而不必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();
}
4

1 回答 1

2

两条评论。

第一个是因为mStopped在一个线程中修改,在其他线程中访问,所以对它的所有访问都必须同步。就目前而言,您的程序具有未定义的行为。这在实践中是否会成为问题,我不知道;先验地,编译器可以推断出stop没有其他线程访问该变量,并且stop在函数返回后调用的线程中没有访问该变量,因此抑制了赋值。

第二个是在我有一个工作列表的情况下,我通常发现创建一个终止线程的特殊工作是最简单的。标准线程似乎没有thread_exit函数,但这可以通过让作业返回布尔值或使用异常来轻松模拟。然后该stop函数锁定互斥体,清空队列,并将这些特殊终止作业之一插入每个线程的队列中。

于 2013-09-24T09:37:39.133 回答