繁体   English   中英

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

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

我已经按照Kerrek SB这个问题中的回答实现了线程池。

我已经为函数实现了 MPMC 队列,并为线程实现了向量线程。

一切都很完美,除了我不知道如何终止程序,最后如果我只是做thread.join因为线程仍在等待更多任务,它不会加入,主线程也不会继续.

知道如何正确结束程序吗?

为了完整起见,这是我的代码:

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();
};

函数池.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
}

主文件

#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;
}

您始终可以使用特定的异常类型向infinite_loop_func发出它应该返回的信号...

class quit_worker_exception: public std::exception {};

然后将infinite_loop_func更改为...

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

通过上述更改,您可以使用(在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();

每个infinite_loop_func实例都会将一个函数对象出列,当调用该函数对象时,会抛出一个quit_worker_exception导致它返回。

您的问题位于infinite_loop_func ,这是一个无限循环,结果不会终止。 我已经阅读了建议抛出异常的先前答案,但是,我不喜欢它,因为异常不应用于常规控制流。

解决此问题的最佳方法是显式处理停止条件。 例如:

std::atomic<bool> acceptsFunctions;

将它添加到函数池可以让您清楚地拥有状态并断言在您销毁时不会添加新函数。

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

返回一个空的可选(或 C++14 及之前的函数),允许您处理一个空队列。 您必须这样做,因为condition_variable可以进行虚假唤醒。

有了这个, m_data_condition.notify_all()可以用来唤醒所有线程。

最后,我们必须修复无限循环,因为它不包括过度使用,同时允许您执行仍在队列中的所有函数:

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 ();
}

我将由您来实现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

#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();
};

函数池.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();
    }
}

主文件

#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