This tutorial demonstrates the use of the boost::asio::strand class to synchronise callback handlers in a multithreaded program.

The previous four tutorials avoided the issue of handler synchronisation by calling the boost::asio::io_service::run() function from one thread only. As you already know, the asio library provides a guarantee that callback handlers will only be called from threads that are currently calling boost::asio::io_service::run(). Consequently, calling boost::asio::io_service::run() from only one thread ensures that callback handlers cannot run concurrently.

The single threaded approach is usually the best place to start when developing applications using asio. The downside is the limitations it places on programs, particularly servers, including:

  • Poor responsiveness when handlers can take a long time to complete.
  • An inability to scale on multiprocessor systems.

If you find yourself running into these limitations, an alternative approach is to have a pool of threads calling boost::asio::io_service::run(). However, as this allows handlers to execute concurrently, we need a method of synchronisation when handlers might be accessing a shared, thread-unsafe resource.

#include <iostream>
#include <boost/asio.hpp>
#include <boost/thread.hpp>
#include <boost/bind.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>

We start by defining a class called printer, similar to the class in the previous tutorial. This class will extend the previous tutorial by running two timers in parallel.

class printer
{
public:

In addition to initialising a pair of boost::asio::deadline_timer members, the constructor initialises the strand_ member, an object of type boost::asio::strand.

An boost::asio::strand guarantees that, for those handlers that are dispatched through it, an executing handler will be allowed to complete before the next one is started. This is guaranteed irrespective of the number of threads that are calling boost::asio::io_service::run(). Of course, the handlers may still execute concurrently with other handlers that were not dispatched through an boost::asio::strand, or were dispatched through a different boost::asio::strand object.

  printer(boost::asio::io_service& io)
: strand_(io),
timer1_(io, boost::posix_time::seconds(1)),
timer2_(io, boost::posix_time::seconds(1)),
count_(0)
{

When initiating the asynchronous operations, each callback handler is "wrapped" using the boost::asio::strand object. The boost::asio::strand::wrap() function returns a new handler that automatically dispatches its contained handler through the boost::asio::strand object. By wrapping the handlers using the same boost::asio::strand, we are ensuring that they cannot execute concurrently.

    timer1_.async_wait(strand_.wrap(boost::bind(&printer::print1, this)));
timer2_.async_wait(strand_.wrap(boost::bind(&printer::print2, this)));
} ~printer()
{
std::cout << "Final count is " << count_ << "\n";
}

In a multithreaded program, the handlers for asynchronous operations should be synchronised if they access shared resources. In this tutorial, the shared resources used by the handlers (print1 and print2) are std::cout and the count_ data member.

  void print1()
{
if (count_ < 10)
{
std::cout << "Timer 1: " << count_ << "\n";
++count_; timer1_.expires_at(timer1_.expires_at() + boost::posix_time::seconds(1));
timer1_.async_wait(strand_.wrap(boost::bind(&printer::print1, this)));
}
} void print2()
{
if (count_ < 10)
{
std::cout << "Timer 2: " << count_ << "\n";
++count_; timer2_.expires_at(timer2_.expires_at() + boost::posix_time::seconds(1));
timer2_.async_wait(strand_.wrap(boost::bind(&printer::print2, this)));
}
} private:
boost::asio::strand strand_;
boost::asio::deadline_timer timer1_;
boost::asio::deadline_timer timer2_;
int count_;
};

The main function now causes boost::asio::io_service::run() to be called from two threads: the main thread and one additional thread. This is accomplished using an boost::thread object.

Just as it would with a call from a single thread, concurrent calls to boost::asio::io_service::run() will continue to execute while there is "work" left to do. The background thread will not exit until all asynchronous operations have completed.

int main()
{
boost::asio::io_service io;
printer p(io);
boost::thread t(boost::bind(&boost::asio::io_service::run, &io));
io.run();
t.join(); return 0;
}

See the full source listing

Timer.5 - Synchronising handlers in multithreaded programs的更多相关文章

  1. Timer.4 - Using a member function as a handler

    In this tutorial we will see how to use a class member function as a callback handler. The program s ...

  2. boost::asio译文

        Christopher Kohlhoff Copyright © 2003-2012 Christopher M. Kohlhoff 以Boost1.0的软件授权进行发布(见附带的LICENS ...

  3. Boost.Asio技术文档

    Christopher Kohlhoff Copyright © 2003-2012 Christopher M. Kohlhoff 以Boost1.0的软件授权进行发布(见附带的LICENSE_1_ ...

  4. 【转】Multithreaded Python Tutorial with the “Threadworms” Demo

    The code for this tutorial can be downloaded here: threadworms.py or from GitHub. This code works wi ...

  5. Introduction to Multi-Threaded, Multi-Core and Parallel Programming concepts

    https://katyscode.wordpress.com/2013/05/17/introduction-to-multi-threaded-multi-core-and-parallel-pr ...

  6. Java性能提示(全)

    http://www.onjava.com/pub/a/onjava/2001/05/30/optimization.htmlComparing the performance of LinkedLi ...

  7. Modules you should know in Python Libray

    前两天被问到常用的python lib和module有哪些?最常用的那几个,其他的一下子竟然回答不上.想想也是,一般情况下,遇到一个问题,在网上一搜,顺着线索找到可用的例子,然后基本没有怎么深究.结果 ...

  8. Google C++ Style Guide

    Background C++ is one of the main development languages used by many of Google's open-source project ...

  9. Programming with gtkmm 3

      https://developer.gnome.org/gtkmm-tutorial/unstable/index.html.zh_CN 1. 序言 1.1. 本书 1.2. gtkmm 2. 安 ...

随机推荐

  1. mount USB Device(U disk) on crux based on vmware

    1. 在 /mnt 下建立一个名叫USB的文件夹,文件夹名自定 cd /mnt mkdir USB 2. 查看一下磁盘分区情况 fdisk –l 3. 插入U盘 4. 再次查看磁盘分区情况,对比第一次 ...

  2. 有关android 应用的plugin框架调研

    1. 借助android提供的shareduserid属性使多个不同的apt共用一个userid,以扫除权限壁垒,获取插件context,继而获取view并加载插件.这种方式是建立在已经安装完成的ap ...

  3. 查看Oracle当前用户下的信息(用户,表视图,索引,表空间,同义词,存储过程函数,约束条件)

    0.表空间 SQL>select username,default_tablespace from user_users; 查看当前用户的角色 SQL>select * from user ...

  4. windows常用net use命令

    net share :查看本地主机的共资源 nbtstat -A IP :得到远程主机的用户列表 net user c:/del 删除映射的C盘,其它盘类推 net user * /del 删除全部映 ...

  5. 关于sed的应用

    公司让我做一个看一下在优化的程序和比原来的程序快多少,但是文件还在运行的服务器上,我需要把用到的文件复制到测试服务器上去.但是测试服务器上有的,目录不全,会导致scp出错.就发生了以下的故事. 首选我 ...

  6. Verilog 读写文件

    Verilog 读写文件 在数字设计验证中,有时我们需要大量的数据,这时可以通过文件输入,有时我们需要保存数据,可以通过写文件保存. 读写文件testbench module file_rw_tb() ...

  7. CentOS 6.8编译安装httpd2.2.31+MySQL5.6.31+PHP5.3.27

    CentOS 6.8编译安装httpd2.2.31+MySQL5.6.31+PHP5.3.27   说明:   操作系统:CentOS 6.8 32位 准备篇: 一.系统约定    软件源代码包存放位 ...

  8. 关于transform的2D

    在transform的学习中,自己总结了一点经验. 我们知道transform有2D和3D的两大类变换,这里分享下关于2D的属性简单示例: 一.2D变换:  (x为水平,y为垂直) 1.skew(斜拉 ...

  9. CSS结构伪类E:first-child/last-child/only-child/empty

    E:first-child解释:E的父元素的第一个子元素正好是E,给这个E定义样式 E:last-child解释:E的父元素的最后一个子元素正好是E,给这个E定义样式 E:only-child解释:E ...

  10. HDU 5044 离线LCA算法

    昨天写了HDU 3966 ,本来这道题是很好解得,结果我想用离线LCA 耍一把,结果发现离线LCA 没理解透,错了好多遍,终得AC ,这题比起 HDU 3966要简单,因为他不用动态查询.但是我还是错 ...