简体   繁体   English

c++中的线程池-如何结束程序

[英]thread pooling in c++ - how to end the program

I've implemented thread pooling following the answer of Kerrek SB in this question .我已经按照Kerrek SB这个问题中的回答实现了线程池。

I've implemented MPMC queue for the functions and vector threads for the threads.我已经为函数实现了 MPMC 队列,并为线程实现了向量线程。

Everything worked perfectly, except that I don't know how to terminate the program, in the end if I just do thread.join since the thread is still waiting for more tasks to do, it will not join and the main thread will not continue.一切都很完美,除了我不知道如何终止程序,最后如果我只是做thread.join因为线程仍在等待更多任务,它不会加入,主线程也不会继续.

Any idea how to end the program correctly?知道如何正确结束程序吗?

For completeness, this is my code:为了完整起见,这是我的代码:

function_pool.h function_pool.h

#pragma once
#include <queue>
#include <functional>
#include <mutex>
#include <condition_variable>

class Function_pool
    {

private:
    std::queue<std::function<void()>> m_function_queue;
    std::mutex m_lock;
    std::condition_variable m_data_condition;

public: 
    Function_pool();
    ~Function_pool();
    void push(std::function<void()> func);
    std::function<void()> pop();
};

function_pool.cpp函数池.cpp

#include "function_pool.h"

Function_pool::Function_pool() : m_function_queue(), m_lock(), m_data_condition()
{
}

Function_pool::~Function_pool()
{
} 

void Function_pool::push(std::function<void()> func)
{
    std::unique_lock<std::mutex> lock(m_lock);
    m_function_queue.push(func);
    // when we send the notification immediately, the consumer will try to 
get the lock , so unlock asap
    lock.unlock();
    m_data_condition.notify_one();
}

std::function<void()> Function_pool::pop()
{
    std::unique_lock<std::mutex> lock(m_lock);
    m_data_condition.wait(lock, [this]() {return !m_function_queue.empty(); 
});
    auto func = m_function_queue.front();
    m_function_queue.pop();
    return func;
    // Lock will be released
}

main.cpp主文件

#include "function_pool.h"
#include <string>
#include <iostream>
#include <mutex>
#include <functional>
#include <thread>
#include <vector>

Function_pool func_pool;

void example_function()
{
    std::cout << "bla" << std::endl;
}

void infinite_loop_func()
{
    while (true)
    {
        std::function<void()> func = func_pool.pop();
        func();
    }
}

int main()
{
    std::cout << "stating operation" << std::endl;
    int num_threads = std::thread::hardware_concurrency();
    std::cout << "number of threads = " << num_threads << std::endl;
    std::vector<std::thread> thread_pool;
    for (int i = 0; i < num_threads; i++)
    {
        thread_pool.push_back(std::thread(infinite_loop_func));
    }

    //here we should send our functions
    func_pool.push(example_function);

    for (int i = 0; i < thread_pool.size(); i++)
    {
        thread_pool.at(i).join();
    }
    int i;
    std::cin >> i;
}

You can always use a specific exception type to signal to infinite_loop_func that it should return...您始终可以使用特定的异常类型向infinite_loop_func发出它应该返回的信号...

class quit_worker_exception: public std::exception {};

Then change infinite_loop_func to...然后将infinite_loop_func更改为...

void infinite_loop_func ()
{
  while (true) {
    std::function<void()> func = func_pool.pop();
    try {
      func();
    }
    catch (quit_worker_exception &ex) {
      return;
    }
  }
}

With the above changes you could then use (in main )...通过上述更改,您可以使用(在main中)...

/*
 * Enqueue `thread_pool.size()' function objects whose sole job is
 * to throw an instance of `quit_worker_exception' when invoked.
 */
for (int i = 0; i < thread_pool.size(); i++)
  func_pool.push([](){ throw quit_worker_exception(); });

/*
 * Now just wait for each worker to terminate having received its
 * quit_worker_exception.
 */
for (int i = 0; i < thread_pool.size(); i++)
  thread_pool.at(i).join();

Each instance of infinite_loop_func will dequeue one function object which, when called, throws a quit_worker_exception causing it to return.每个infinite_loop_func实例都会将一个函数对象出列,当调用该函数对象时,会抛出一个quit_worker_exception导致它返回。

Your problem is located in infinite_loop_func , which is an infinite loop and by result doesn't terminate.您的问题位于infinite_loop_func ,这是一个无限循环,结果不会终止。 I've read the previous answer which suggests throwing an exception, however, I don't like it since exceptions should not be used for the regular control flow.我已经阅读了建议抛出异常的先前答案,但是,我不喜欢它,因为异常不应用于常规控制流。

The best way to solve this is to explicitly deal with the stop condition.解决此问题的最佳方法是显式处理停止条件。 For example:例如:

std::atomic<bool> acceptsFunctions;

Adding this to the function pool allows you to clearly have state and to assert that no new functions being added when you destruct.将它添加到函数池可以让您清楚地拥有状态并断言在您销毁时不会添加新函数。

std::optional<std::function<void()>> Function_pool::pop()

Returning an empty optional (or function in C++14 and before), allows you to deal with an empty queue.返回一个空的可选(或 C++14 及之前的函数),允许您处理一个空队列。 You have to, as condition_variable can do spurious wakeups.您必须这样做,因为condition_variable可以进行虚假唤醒。

With this, m_data_condition.notify_all() can be used to wake all threads.有了这个, m_data_condition.notify_all()可以用来唤醒所有线程。

Finally we have to fix the infinite loop as it doesn't cover overcommitment and at the same time allows you to execute all functions still in the queue:最后,我们必须修复无限循环,因为它不包括过度使用,同时允许您执行仍在队列中的所有函数:

while (func_pool.acceptsFunctions || func_pool.containsFunctions())
{
    auto f = func_pool.pop();
    If (!f)
    {
           func_pool.m_data_condition.wait_for(1s);
            continue;
     }

    auto &function = *f;
    function ();
}

I'll leave it up to you to implement containsFunctions() and clean up the code (infinite_loop_func as member function?) Note that with a counter, you could even deal with background task being spawned.我将由您来实现containsFunctions()并清理代码(infinite_loop_func 作为成员函数?)请注意,使用计数器,您甚至可以处理生成的后台任务。

Follwoing [JVApen](https://stackoverflow.com/posts/51382714/revisions) suggestion, I copy my code in case anyone will want a working code:

function_pool.h function_pool.h

#pragma once
#include <queue>
#include <functional>
#include <mutex>
#include <condition_variable>
#include <atomic>
#include <cassert>

class Function_pool
{

private:
    std::queue<std::function<void()>> m_function_queue;
    std::mutex m_lock;
    std::condition_variable m_data_condition;
    std::atomic<bool> m_accept_functions;

public:

    Function_pool();
    ~Function_pool();
    void push(std::function<void()> func);
    void done();
    void infinite_loop_func();
};

function_pool.cpp函数池.cpp

#include "function_pool.h"

Function_pool::Function_pool() : m_function_queue(), m_lock(), m_data_condition(), m_accept_functions(true)
{
}

Function_pool::~Function_pool()
{
}

void Function_pool::push(std::function<void()> func)
{
    std::unique_lock<std::mutex> lock(m_lock);
    m_function_queue.push(func);
    // when we send the notification immediately, the consumer will try to get the lock , so unlock asap
    lock.unlock();
    m_data_condition.notify_one();
}

void Function_pool::done()
{
    std::unique_lock<std::mutex> lock(m_lock);
    m_accept_functions = false;
    lock.unlock();
    // when we send the notification immediately, the consumer will try to get the lock , so unlock asap
    m_data_condition.notify_all();
    //notify all waiting threads.
}

void Function_pool::infinite_loop_func()
{
    std::function<void()> func;
    while (true)
    {
        {
            std::unique_lock<std::mutex> lock(m_lock);
            m_data_condition.wait(lock, [this]() {return !m_function_queue.empty() || !m_accept_functions; });
            if (!m_accept_functions && m_function_queue.empty())
            {
                //lock will be release automatically.
                //finish the thread loop and let it join in the main thread.
                return;
            }
            func = m_function_queue.front();
            m_function_queue.pop();
            //release the lock
        }
        func();
    }
}

main.cpp主文件

#include "function_pool.h"
#include <string>
#include <iostream>
#include <mutex>
#include <functional>
#include <thread>
#include <vector>

Function_pool func_pool;

class quit_worker_exception : public std::exception {};

void example_function()
{
    std::cout << "bla" << std::endl;
}

int main()
{
    std::cout << "stating operation" << std::endl;
    int num_threads = std::thread::hardware_concurrency();
    std::cout << "number of threads = " << num_threads << std::endl;
    std::vector<std::thread> thread_pool;
    for (int i = 0; i < num_threads; i++)
    {
        thread_pool.push_back(std::thread(&Function_pool::infinite_loop_func, &func_pool));
    }

    //here we should send our functions
    for (int i = 0; i < 50; i++)
    {
        func_pool.push(example_function);
    }
    func_pool.done();
    for (unsigned int i = 0; i < thread_pool.size(); i++)
    {
        thread_pool.at(i).join();
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM