为什么我的Wait方法无限期挂起?

时间:2016-01-19 02:55:55

标签: c++ multithreading boost

我正在使用这段代码来处理一个线程池。在多次调用RunTask之后(有问题的任务是一个休眠5秒的方法),然后我调用Wait()试图等待所有线程完成。代码在Wait()mehhod中挂起。我怀疑它是我对条件变量的管理,但我无法弄明白。

class lthThreadHandler {
public:
    lthThreadHandler(int pool_size)
    {
        mAvailable = pool_size;
        mRunning = true;
        for (int i = 0; i < pool_size; ++i)
            mThreads.create_thread(boost::bind(&lthThreadHandler::pool_main, this));
    }

    template<typename Task>
    void RunTask(Task task)
    {
        boost::unique_lock<boost::mutex> lock(mMutex);

        // If no threads are available, then return.
        if (0 == mAvailable)
            return;

        // Decrement count, indicating thread is no longer available.
        --mAvailable;

        // Set task and signal condition variable so that a worker thread will
        // wake up andl use the task.
        mTasks.push(boost::function<void()>(task));
        mCondition.notify_one();
    }

    void Wait() {
        try {
            mThreads.join_all();
        }
        // Suppress all exceptions.
        catch (...) {
        }
    }

    /// @brief Entry point for pool threads.
    void pool_main() {
        while (mRunning) {
            // Wait on condition variable while the task is empty and the pool is
            // still running.
            boost::unique_lock<boost::mutex> lock(mMutex);
            while (mTasks.empty() && mRunning) {
                mCondition.wait(lock);
            }
            // If pool is no longer running, break out.
            if (!mRunning)
                break;

            // Copy task locally and remove from the queue.  This is done within
            // its own scope so that the task object is destructed immediately
            // after running the task.  This is useful in the event that the
            // function contains shared_ptr arguments bound via bind.
            {
                boost::function<void()> task = mTasks.front();
                mTasks.pop();

                lock.unlock();

                // Run the task.
                try {
                    task();
                }
                // Suppress all exceptions.
                catch (...) {
                }
            }

            // Task has finished, so increment count of available threads.
            lock.lock();
            ++mAvailable;
        } // while mRunning
    }
private:
    std::queue<boost::function<void()> > mTasks;
    boost::thread_group mThreads;
    std::size_t mAvailable;
    boost::mutex mMutex;
    boost::condition_variable mCondition;
    bool mRunning;
};

此代码段来自另一个SO答案:Thread pool using boost asio

1 个答案:

答案 0 :(得分:0)

Wait无限期挂起,因为只要你不清除mRunning,线程都会运行无限循环。即使你因为数据争用而做it might change your pet into a bag of popcorn

因此。基本上你诅咒某人完美的taskqueue / threadpool实现。主要是通过闲置重命名¹然后

  • 删除关键的析构函数
  • 添加一个只加入线程的Wait(),而不做任何事情导致这些线程完成。
  • mRunning旗帜
  • 上添加竞争条件

即使您将原始析构函数逻辑放入Wait(),请记住,您实际上会遇到竞争条件,即在(主)线程重置{{1}之前无法保证已发布的任务将运行国旗。

现在怎么办?

我建议你不要干涉代码。

  • 如果有什么需要它做的话,请问一个建设性的问题,比如“我怎样才能确保XXX”,而不是“为什么我的破码不是YYY”
  • 如果您希望学习询问原始实施,或关于单个孤立的更改

替代

我有一些类似的线程,你可能会发现[SO]上更容易理解,所以你可以比较一下:

之前我曾回答过一个类似的问题(回应你从成员函数而不是析构函数中尝试mRunning的事实:

这个答案对你解释为什么这种功能很棘手很有帮助。

¹join_all,真的吗?线程管理员,真的吗?所有的名字最初选择得更好。