首页 > 解决方案 > 运行相同 io_service 的两个线程

问题描述

https://youtu.be/rwOv_tw2eA4?t=1030

这个例子有一个 io_service 和两个线程在上面运行。

void timer_expired( std:: string id )
{
    std::cout << timestamp() << ": " << id << ": begin\n";
    std::this_thread::sleep_for( std::chrono::seconds(3) );
    std::cout << timestamp() << ": " << id << ": end\n";
}

int main()
{
    boost::asio::io_service io_service;

    boost::asio::deadline_timer timer1( io_service, boost::posix_time::seconds(5) );
    boost::asio::deadline_timer timer2( io_service, boost::posix_time::seconds(5) );

    timer1.async_wait( []( auto ... ){ timer_expired("timer1"); });
    timer2.async_wait( []( auto ... ){ timer_expired("timer2"); });


    std::cout << timestamp() << ": calling io_service run\n";

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

    thread1.join();
    thread2.join();

    std::cout << timestamp() << ": done\n";

    return 0;
}

每次我运行这个示例时,输出看起来都不错,因为:

作者说,这段代码有比赛,不应该工作(车库输出)。

不太清楚的是我们有两个线程,每个线程可以服务一个完成处理程序(这里是计时器回调)。那么为什么要比赛呢?事实上,我多次运行此代码并且无法产生作者提出的任何垃圾输出。

输出看起来符合预期,这是一个示例:

2019-07-28 11:27:44: calling io_service run
2019-07-28 11:27:49: timer1: begin
2019-07-28 11:27:49: timer2: begin
2019-07-28 11:27:52: timer1: end
2019-07-28 11:27:52: timer2: end
2019-07-28 11:27:52: done

标签: c++11boost-asio

解决方案


处理程序在io_service::run. 您启动了两个io.run()有效的线程。所以你同时有两个运行方法timer_expired。比赛是在访问cout流时进行的。

你很幸运能看到这个漂亮的输出,但是当你添加更多作品时timer_expired

void timer_expired( std:: string id )
{
    std::cout << timestamp() << ": " << id << ": begin\n";
    std::this_thread::sleep_for( std::chrono::seconds(3) );
    // ADD MORE LINES TO BE PRINTED
    for (int i = 0; i < 1000; ++i)
        std::cout << timestamp() << ": " << id << ": end" << std::endl;
}

你会看到交错的字符。

多线程cout对象访问不会导致崩溃(根据参考),

多线程对同步 ([ios.members.static]) 标准 iostream 对象的格式化和未格式化输入和输出函数或标准 C 流的并发访问不应导致数据竞争。[ 注意:如果用户希望避免交错字符,则仍必须同步多个线程对这些对象和流的并发使用。

但是为了避免这些交错的字符,您必须在访问时添加同步cout,例如通过使用std::mutex,或以串行方式调用处理程序 - 使用 boost.js 中的strand对象。


推荐阅读