使用boost :: asio时是否需要实现阻塞?

时间:2018-01-29 18:38:03

标签: c++ boost boost-asio

我的问题是,如果我在多个线程上运行io_service :: run(),我是否需要在这些异步函数上实现阻塞?

示例:

int i = 0;
int j = 0;

void test_timer(boost::system::error_code ec)
{
    //I need to lock up here ?
    if (i++ == 10)
    {
        j = i * 10;
    }
    timer.expires_at(timer.expires_at() + boost::posix_time::milliseconds(500));
    timer.async_wait(&test_timer);
}

void threadMain()
{
    io_service.run();
}

int main()
{
    boost::thread_group workers;
    timer.async_wait(&test_timer);

    for (int i = 0; i < 5; i++){
        workers.create_thread(&threadMain);
    }

    io_service.run();
    workers.join_all();
    return 0;
}

1 个答案:

答案 0 :(得分:2)

异步的定义是它是非阻塞的。

如果您要问“我是否必须同步访问来自不同线程的共享对象” - 该问题是无关的,答案取决于您共享的对象记录的线程安全性。

对于Asio,基本上(粗略总结)您需要将并发访问(从多个线程并发)与所有类型同步,除boost::asio::io_context¹,²。

您的样本

您的示例使用运行io服务的多个线程,这意味着处理程序可以在任何这些线程上运行。这意味着有效地分享全局变量,实际上它们需要保护。

但是 因为您的应用程序逻辑(异步调用链)指示只有一个操作处于挂起状态,并且共享计时器对象上的下一个异步操作是始终从该链中安排,从一个线程(称为隐含链)访问逻辑。请参阅Why do I need strand per connection when using boost::asio?

最简单的方法:

Logical Strand

<强> Live On Coliru

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

boost::asio::io_service io_service;
boost::asio::deadline_timer timer { io_service };

struct state_t {
    int i = 0;
    int j = 0;
} state;

void test_timer(boost::system::error_code ec)
{
    if (ec != boost::asio::error::operation_aborted) {
        {
            if (state.i++ == 10) {
                state.j = state.i * 10;
                if (state.j > 100)
                    return; // stop after 5 seconds
            }
        }
        timer.expires_at(timer.expires_at() + boost::posix_time::milliseconds(50));
        timer.async_wait(&test_timer);
    }
}

int main()
{
    boost::thread_group workers;
    timer.expires_from_now(boost::posix_time::milliseconds(50));
    timer.async_wait(&test_timer);

    for (int i = 0; i < 5; i++){
        workers.create_thread([] { io_service.run(); });
    }

    workers.join_all();
    std::cout << "i = " << state.i << std::endl;
    std::cout << "j = " << state.j << std::endl;
}
  

注意我从主线程中移除了io_service::run(),因为join()是多余的(除非你真的想要 6 运行处理程序的线程,而不是5)。

打印

i = 11
j = 110

买者

潜伏在这里的陷阱。说,你不想像我一样保释,但想要停下来,你很想做:

timer.cancel();

来自main。那是合法,因为deadline_timer对象线程安全。你需要

  • 使用全局atomic_bool来表示终止请求
  • timer.cancel()发布在与定时器异步链相同的 strand 上。但是,只有一个显式链,因此如果不更改代码以使用显式链,则无法执行此操作。

更多计时器

让我们通过两个定时器来复杂化,它们有自己的隐含线。这意味着仍然无需同步对计时器实例的访问,但需要访问ij

  

注意在本演示中,我使用synchronized_value<>来表达优雅。您可以使用mutexlock_guard手动编写类似的逻辑。

<强> Live On Coliru

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

boost::asio::io_service io_service;

struct state {
    int i = 0;
    int j = 0;
};

boost::synchronized_value<state> shared_state;

struct TimerChain {
    boost::asio::deadline_timer _timer;

    TimerChain() : _timer{io_service} {
        _timer.expires_from_now(boost::posix_time::milliseconds(50));
        resume();
    }

    void resume() {
        _timer.async_wait(boost::bind(&TimerChain::test_timer, this, _1));
    };

    void test_timer(boost::system::error_code ec)
    {
        if (ec != boost::asio::error::operation_aborted) {
            {
                auto state = shared_state.synchronize();
                if (state->i++ == 10) {
                    state->j = state->i * 10;
                }
                if (state->j > 100) return; // stop after some iterations
            }
            _timer.expires_at(_timer.expires_at() + boost::posix_time::milliseconds(50));
            resume();
        }
    }
};

int main()
{
    boost::thread_group workers;
    TimerChain timer1;
    TimerChain timer2;

    for (int i = 0; i < 5; i++){
        workers.create_thread([] { io_service.run(); });
    }

    workers.join_all();
    auto state = shared_state.synchronize();
    std::cout << "i = " << state->i << std::endl;
    std::cout << "j = " << state->j << std::endl;
}

打印

i = 12
j = 110

添加显式链

现在添加它们非常简单:

struct TimerChain {
    boost::asio::io_service::strand _strand;
    boost::asio::deadline_timer _timer;

    TimerChain() : _strand{io_service}, _timer{io_service} {
        _timer.expires_from_now(boost::posix_time::milliseconds(50));
        resume();
    }

    void resume() {
        _timer.async_wait(_strand.wrap(boost::bind(&TimerChain::test_timer, this, _1)));
    };

    void stop() { // thread safe
        _strand.post([this] { _timer.cancel(); });
    }

    // ...

<强> Live On Coliru

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

boost::asio::io_service io_service;

struct state {
    int i = 0;
    int j = 0;
};

boost::synchronized_value<state> shared_state;

struct TimerChain {
    boost::asio::io_service::strand _strand;
    boost::asio::deadline_timer _timer;

    TimerChain() : _strand{io_service}, _timer{io_service} {
        _timer.expires_from_now(boost::posix_time::milliseconds(50));
        resume();
    }

    void resume() {
        _timer.async_wait(_strand.wrap(boost::bind(&TimerChain::test_timer, this, _1)));
    };

    void stop() { // thread safe
        _strand.post([this] { _timer.cancel(); });
    }

    void test_timer(boost::system::error_code ec)
    {
        if (ec != boost::asio::error::operation_aborted) {
            {
                auto state = shared_state.synchronize();
                if (state->i++ == 10) {
                    state->j = state->i * 10;
                }
            }
            // continue indefinitely
            _timer.expires_at(_timer.expires_at() + boost::posix_time::milliseconds(50));
            resume();
        }
    }
};

int main()
{
    boost::thread_group workers;
    TimerChain timer1;
    TimerChain timer2;

    for (int i = 0; i < 5; i++){
        workers.create_thread([] { io_service.run(); });
    }

    boost::this_thread::sleep_for(boost::chrono::seconds(10));
    timer1.stop();
    timer2.stop();

    workers.join_all();

    auto state = shared_state.synchronize();
    std::cout << "i = " << state->i << std::endl;
    std::cout << "j = " << state->j << std::endl;
}

打印

i = 400
j = 110

¹(或使用遗留名称boost::asio::io_service

在这方面,

²生命期突变不被视为成员操作(即使对于线程安全的对象,您也必须手动同步共享对象的构造/销毁)