Как работает asio::strand?

Рейтинг: 1Ответов: 1Опубликовано: 28.04.2023

Доброго времени суток.

Если я правильно понимаю, то asio::strand нужен для последовательного выполнения связанных операций.

Но у меня операции все равно перемешиваются.

m 140460063954752
0 140460063950400
1 140460063950400
2 140460055557696
4 140460055557696
3 140460063950400
6 140460063950400
7 140460063950400
8 140460063950400
9 140460063950400
5 140460055557696

Вопрос: что я делаю не так?

Код:

#include <iostream>

#include <thread>
#include <mutex>
#include <chrono>
#include <boost/asio.hpp>

namespace asio = boost::asio;
namespace io = asio;
namespace ip = io::ip;

using namespace std::chrono_literals;

void run_pool(asio::io_context& pool, std::size_t n)
{
    std::vector<std::thread> jobs;
    jobs.reserve(n);
    for (std::size_t i = 0; i < n; ++i)
        jobs.emplace_back([&pool]{ pool.run(); });
    for (std::size_t i = 0; i < n; ++i)
        jobs[i].join();
}

void run_pool(asio::thread_pool& pool, std::size_t /*n*/)
{
    pool.wait();
}

int main()
{
    using pool_t = std::conditional_t<false, asio::thread_pool, asio::io_context>;
    int n_t = static_cast<int>(std::thread::hardware_concurrency());
    std::cout << "num threads: " << n_t << std::endl;
    pool_t pool{};
    std::mutex mtx_io;
    auto strand = asio::make_strand(pool);
    std::cout << "m " << std::this_thread::get_id() << std::endl;
    for (std::size_t n = 0; n < 10; ++n) {
        auto token = [n, &mtx_io] {
            std::cout << n << " " << std::this_thread::get_id() << std::endl;
        };
        asio::post(pool, asio::bind_executor(strand, std::move(token)));
    }
    run_pool(pool, n_t);
    return 0;
}

Ожидаемое поведение: n выводится последовательно

Текущее поведение: n выводится вперемешку

Пробовал asio 1.24 и boost::asio 1.82, тестирую на Linux Mint

Ответы

▲ 3Принят

asio::strand нужен для исключения конкурентного осуществления операций. По сути, это аналог mutex - среди всего пула потоков операции, относящиеся к одному strand, в любой момент времени будет выполнять не более одного потока. На то, какие потоки и в какой последовательности будут выполнять эти операции, оно не влияет. Преимущество по сравнению с использованием mutex внутри самой операции в том, что вместо простоя при заблокированном mutex, обработчик может заниматься разбором операций, относящихся к другим strand.