Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/157.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 高效地等待线程池中的所有任务完成_C++_Multithreading_Threadpool_Wait_Stdthread - Fatal编程技术网

C++ 高效地等待线程池中的所有任务完成

C++ 高效地等待线程池中的所有任务完成,c++,multithreading,threadpool,wait,stdthread,C++,Multithreading,Threadpool,Wait,Stdthread,目前,我的线程池中有一个包含x工作者的程序。在主循环期间,y任务分配给工人完成,但在任务发出后,我必须等待所有任务完成,然后才能开始执行程序。我认为我目前的解决方案效率低下,必须有更好的方法等待所有任务完成,但我不确定如何进行 // called in main after all tasks are enqueued to // std::deque<std::function<void()>> tasks void ThreadPool::waitFinished(

目前,我的线程池中有一个包含x工作者的程序。在主循环期间,y任务分配给工人完成,但在任务发出后,我必须等待所有任务完成,然后才能开始执行程序。我认为我目前的解决方案效率低下,必须有更好的方法等待所有任务完成,但我不确定如何进行

// called in main after all tasks are enqueued to 
// std::deque<std::function<void()>> tasks
void ThreadPool::waitFinished()
{
    while(!tasks.empty()) //check if there are any tasks in queue waiting to be picked up
    {
        //do literally nothing
    }
}

这样我就可以发送成百上千的粒子位置任务,让它们并行完成,等待它们完成,然后将新数据放入openGL位置缓冲区中这是一种尝试的方法。在同一互斥体上使用两个条件变量不适合轻松的人,除非您知道内部发生了什么。我不需要原子处理成员,只需要演示每次运行之间完成了多少项

本文中的示例workload函数生成一百万个随机int值,然后对它们进行排序(以某种方式加热我的办公室)
waitFinished
将不会返回,直到队列为空且没有线程处于忙碌状态

#include <iostream>
#include <deque>
#include <functional>
#include <thread>
#include <condition_variable>
#include <mutex>
#include <random>

//thread pool
class ThreadPool
{
public:
    ThreadPool(unsigned int n = std::thread::hardware_concurrency());

    template<class F> void enqueue(F&& f);
    void waitFinished();
    ~ThreadPool();

    unsigned int getProcessed() const { return processed; }

private:
    std::vector< std::thread > workers;
    std::deque< std::function<void()> > tasks;
    std::mutex queue_mutex;
    std::condition_variable cv_task;
    std::condition_variable cv_finished;
    std::atomic_uint processed;
    unsigned int busy;
    bool stop;

    void thread_proc();
};

ThreadPool::ThreadPool(unsigned int n)
    : busy()
    , processed()
    , stop()
{
    for (unsigned int i=0; i<n; ++i)
        workers.emplace_back(std::bind(&ThreadPool::thread_proc, this));
}

ThreadPool::~ThreadPool()
{
    // set stop-condition
    std::unique_lock<std::mutex> latch(queue_mutex);
    stop = true;
    cv_task.notify_all();
    latch.unlock();

    // all threads terminate, then we're done.
    for (auto& t : workers)
        t.join();
}

void ThreadPool::thread_proc()
{
    while (true)
    {
        std::unique_lock<std::mutex> latch(queue_mutex);
        cv_task.wait(latch, [this](){ return stop || !tasks.empty(); });
        if (!tasks.empty())
        {
            // got work. set busy.
            ++busy;

            // pull from queue
            auto fn = tasks.front();
            tasks.pop_front();

            // release lock. run async
            latch.unlock();

            // run function outside context
            fn();
            ++processed;

            latch.lock();
            --busy;
            cv_finished.notify_one();
        }
        else if (stop)
        {
            break;
        }
    }
}

// generic function push
template<class F>
void ThreadPool::enqueue(F&& f)
{
    std::unique_lock<std::mutex> lock(queue_mutex);
    tasks.emplace_back(std::forward<F>(f));
    cv_task.notify_one();
}

// waits until the queue is empty.
void ThreadPool::waitFinished()
{
    std::unique_lock<std::mutex> lock(queue_mutex);
    cv_finished.wait(lock, [this](){ return tasks.empty() && (busy == 0); });
}

// a cpu-busy task.
void work_proc()
{
    std::random_device rd;
    std::mt19937 rng(rd());

    // build a vector of random numbers
    std::vector<int> data;
    data.reserve(100000);
    std::generate_n(std::back_inserter(data), data.capacity(), [&](){ return rng(); });
    std::sort(data.begin(), data.end(), std::greater<int>());
}

int main()
{
    ThreadPool tp;

    // run five batches of 100 items
    for (int x=0; x<5; ++x)
    {
        // queue 100 work tasks
        for (int i=0; i<100; ++i)
            tp.enqueue(work_proc);

        tp.waitFinished();
        std::cout << tp.getProcessed() << '\n';
    }

    // destructor will close down thread pool
    return EXIT_SUCCESS;
}

祝你好运。

你可以使用and。@WilliamAndrewMontgomery我尝试过这样做,但我需要一个互斥,不是吗?这将锁定停止所有其他线程的任务在windows中,您可以使用CreateThread()返回的句柄数组,然后对该数组使用WaitForMultipleObjects()等待所有线程终止(作为单个原子操作)。我想知道在STL中是否有与此等效的工具。或者更好的方法是,保留一个包含所有工作线程的
std::vector
,每个工作线程都在
waitFinished
中。确实如此。关于Worker的用途还不清楚,但是调用函数对象队列背后的想法肯定已经足够清楚了。这应该是直截了当的。除非有人比我先到,否则我会看看能为你做些什么。重要的是要记住cv+mtx的目的。他们不是为了保持状态;它们用于保护谓词数据(即真实状态)并向其中的数据发送更改信号。无论如何,给我几分钟。我只是好奇,我能把一个lamba函数传递到队列中吗?我的函数需要3-4个变量。谢谢ton@SyntacticFructose当然,只要它符合
void()
。否则,将参数打包并解包到一个具有延迟分派功能的实体中会变得复杂(但仍然可行)。改变工作进程可能最好使用一个简单的对象,它公开
操作符()
(我想这就是你的
Worker
类的全部内容,现在我想起来了)。嗯,我的程序挂起了,我相信这是因为我的lamba是[&](){/…]并且使用了循环中的一对变量。这可能是原因吗?看起来你的代码很有效,所以又是我的实现。我把它设为赏金+50,你帮了很多忙,因为
busy
在不受
queue\u mutex
保护的情况下减少了
busy
:当
busy
的此修改和对
notify\u one()
的以下调用在
waitFinished()
中的lambda条件计算完成后立即发生(即到
false
)并且就在实际等待开始之前,通知将丢失,
waitFinished()的调用方
将永远等待。我认为这就是@SyntacticFructose遇到挂起程序的原因。也许你可以相应地编辑你本来很好的答案。@ph4nt0m你完全正确。我将不得不在成员mods之前对其进行修改和锁定。感谢你关注这一点,并感谢所有为你的评论打勾的人(我加入了他们=P)。
while(running)
    //....
    for all particles alive
        push particle position function to threadpool
    end for

    threadPool.waitFinished();

    push new particle position data into openGL buffer
end while
#include <iostream>
#include <deque>
#include <functional>
#include <thread>
#include <condition_variable>
#include <mutex>
#include <random>

//thread pool
class ThreadPool
{
public:
    ThreadPool(unsigned int n = std::thread::hardware_concurrency());

    template<class F> void enqueue(F&& f);
    void waitFinished();
    ~ThreadPool();

    unsigned int getProcessed() const { return processed; }

private:
    std::vector< std::thread > workers;
    std::deque< std::function<void()> > tasks;
    std::mutex queue_mutex;
    std::condition_variable cv_task;
    std::condition_variable cv_finished;
    std::atomic_uint processed;
    unsigned int busy;
    bool stop;

    void thread_proc();
};

ThreadPool::ThreadPool(unsigned int n)
    : busy()
    , processed()
    , stop()
{
    for (unsigned int i=0; i<n; ++i)
        workers.emplace_back(std::bind(&ThreadPool::thread_proc, this));
}

ThreadPool::~ThreadPool()
{
    // set stop-condition
    std::unique_lock<std::mutex> latch(queue_mutex);
    stop = true;
    cv_task.notify_all();
    latch.unlock();

    // all threads terminate, then we're done.
    for (auto& t : workers)
        t.join();
}

void ThreadPool::thread_proc()
{
    while (true)
    {
        std::unique_lock<std::mutex> latch(queue_mutex);
        cv_task.wait(latch, [this](){ return stop || !tasks.empty(); });
        if (!tasks.empty())
        {
            // got work. set busy.
            ++busy;

            // pull from queue
            auto fn = tasks.front();
            tasks.pop_front();

            // release lock. run async
            latch.unlock();

            // run function outside context
            fn();
            ++processed;

            latch.lock();
            --busy;
            cv_finished.notify_one();
        }
        else if (stop)
        {
            break;
        }
    }
}

// generic function push
template<class F>
void ThreadPool::enqueue(F&& f)
{
    std::unique_lock<std::mutex> lock(queue_mutex);
    tasks.emplace_back(std::forward<F>(f));
    cv_task.notify_one();
}

// waits until the queue is empty.
void ThreadPool::waitFinished()
{
    std::unique_lock<std::mutex> lock(queue_mutex);
    cv_finished.wait(lock, [this](){ return tasks.empty() && (busy == 0); });
}

// a cpu-busy task.
void work_proc()
{
    std::random_device rd;
    std::mt19937 rng(rd());

    // build a vector of random numbers
    std::vector<int> data;
    data.reserve(100000);
    std::generate_n(std::back_inserter(data), data.capacity(), [&](){ return rng(); });
    std::sort(data.begin(), data.end(), std::greater<int>());
}

int main()
{
    ThreadPool tp;

    // run five batches of 100 items
    for (int x=0; x<5; ++x)
    {
        // queue 100 work tasks
        for (int i=0; i<100; ++i)
            tp.enqueue(work_proc);

        tp.waitFinished();
        std::cout << tp.getProcessed() << '\n';
    }

    // destructor will close down thread pool
    return EXIT_SUCCESS;
}
100
200
300
400
500