Ускорьте работу ASIO: дождитесь завершения отложенных задач - PullRequest
0 голосов
/ 06 февраля 2020

Можно ли узнать, когда все ожидающие асинхронные задачи в strand выполнены? Как и thread.join() в следующем примере:

io_service service;
std::thread thread([&](){ service.run(); });

service.post(some_function);
service.post(another_function);

service.stop();
thread.join();

Это было бы полезно для эффективного выполнения нескольких задач в нескольких потоках. Каждое задание является более сложным, чем обычная функция, и имеет свою нить. Но я не нашел способа подождать, пока он не заработает. Я попытался post обработчику финализации цепочки в надежде, что он будет вызван последним, но порядок обработчиков в цепочке не определен, поэтому он запускается немедленно.

Код с цепочкой будет:

io_service service;
strand<io_context::executor_type> strand(make_strand(service));
std::thread thread([&](){ service.run(); });

post(strand, some_function);
post(strand, another_function);

// here we want to wait for strand to complete pending tasks

// somewhere else later
service.stop();
thread.join();

Спасибо.

1 Ответ

1 голос
/ 06 февраля 2020

Один из способов достижения этого - думать об этом с другой стороны:

  • отправить работу исполнителю
  • запускать исполнителя до тех пор, пока вся работа не будет завершена.

Пример:

#include <iostream>
#include <boost/asio.hpp>
#include <thread>
#include <utility>

void some_function()
{
    std::cout << __func__ << std::endl;
}

void another_function()
{
    std::cout << __func__ << std::endl;
}

namespace {

    using namespace boost::asio;

    void test(executor exec)
    {
        post(exec, some_function);
        post(exec, another_function);
    }
}

int main()
{
    auto ioc = boost::asio::io_context();
    auto strand = boost::asio::make_strand(ioc.get_executor());

    // post the work before starting the thread
    test(strand);

    // now run the io_context
    auto t = std::thread([&](){ ioc.run(); });


    // wait for work to finish

    if (t.joinable())
        t.join();

    // thread's io_context has run out of work
}

Другой способ - использовать нить на system_executor.

Системные исполнители выполняют работу с пулом фоновых потоков (если вы отправьте им сообщение), поэтому вам нужно будет предоставить собственный сигнал о завершении работы:

#include <iostream>
#include <boost/asio.hpp>
#include <future>
#include <utility>

void some_function()
{
    std::cout << __func__ << std::endl;
}

void another_function()
{
    std::cout << __func__ << std::endl;
}

namespace {

    using namespace boost::asio;

    // note: a strand models an executor
    auto test(executor exec) -> std::future<void>
    {
        post(exec, some_function);
        post(exec, another_function);

        auto p = std::promise<void>();
        auto f = p.get_future();
        post(exec, [p = std::move(p)]() mutable {
            p.set_value();
        });
        return f;
    }
}

int main()
{
    // note: strand created here
    auto strand = boost::asio::make_strand(boost::asio::system_executor());

    auto f = test(strand);

    // wait for work to finish

    f.get();

    // thread's work is done
}
...