如果在 boost::asio::thread_pool::join 已经工作时调用 boost::asio::post 会发生什么?

What will happen if boost::asio::post is called when boost::asio::thread_pool::join is already working?

我有一个 class 包装 boost::asio::thread_pool m_pool。在包装器的析构函数中,我加入了所有线程:

ThreadPool::~ThreadPool()
{
    m_pool.join();
    cout << "All threads in Thread pool were completed";
}

我还有队列方法来向线程池添加新任务:

void ThreadPool::queue(std::function<void()> task, std::string label)
{
    boost::asio::post(m_pool, task);
    cout << "In Thread pool was enqueued task: " << label;
}

如果 boost::asio::post 被另一个线程调用,当 thread_pool 正在等待加入所有线程时会发生什么情况 在析构函数中?

这个行为有定义吗?会发生什么?任务会完成吗?我猜是。但我不确定。

是的,可以在加入挂起时发布新的处理程序。 join 将一直等待,直到执行上下文用完工作。这可能是不确定的。

事实上,它通常是不确定的,例如在网络服务器的情况下。一旦服务器停止侦听并且所有连接的会话都关闭,执行上下文通常只会“退出”。

换句话说,join()是一个synchronizing/rendez-vous操作,而不是stop()

展示that/how 有效的最简单示例:

Live On Coliru

#include <boost/asio.hpp>
#include <iomanip>
#include <iostream>
using boost::asio::thread_pool;

// convenience
using namespace std::chrono_literals;
static auto now = std::chrono::steady_clock::now;
static auto start = now();

static inline void trace(auto const&... msg) {
    std::cout << std::setw(5) << (now() - start) / 1ms << "ms - ";

    (std::cout << ... << msg) << std::endl;
}

void do_loop(int const i, thread_pool::executor_type ex) {
    std::this_thread::sleep_for(1s);
    trace("work ", i);

    if (i < 10)
        post(ex, std::bind(do_loop, i + 1, ex));
}

int main() {
    boost::asio::thread_pool io;

    post(io, std::bind(do_loop, 0, io.get_executor()));

    trace("Start join");
    io.join();
    trace("Complete join");
}

这会打印出例如

    0ms - Start join
 1001ms - work 0
 2001ms - work 1
 3001ms - work 2
 4002ms - work 3
 5002ms - work 4
 6002ms - work 5
 7002ms - work 6
 8003ms - work 7
 9003ms - work 8
10003ms - work 9
11004ms - work 10
11004ms - Complete join

警告

请注意,如果第一个异步工作是从另一个线程发布的,您可能需要额外的工作来防止池过早加入。

如果没有任何同步,您将无法判断事情的执行顺序:post happens-before join.

在这种情况下,您可以使用 executor_work_guard(参见 make_work_guard)。

The same caveat applies to hybrid scenarios, where some async call chains might terminate, but unrelated threads are still posting tasks without synchronization. The order in which these operations happen is undeterministic (OS/implementation defined) and it may happen to extend the life of the pool.

Again

  • make sure of work to guarantee the task will still execute.
  • keep a separate flag (e.g. an std::atomic_bool) indicating that no more work is being accepted by the pool

这个有记录吗

Yes:

This function blocks until the threads in the pool have completed. If stop() is not called prior to join(), the join() call will wait until the pool has no more outstanding work.