4. 多线程

一般情况下,服务端开启一条线程做io_service::run()工作就足够了。但是,有些情况下可能会变得很糟糕。

从之前的分析,我们知道异步操作的一个关键步骤就是io_service回调我们注册的handler。现在假设客户端与服务端建立了四个socket连接,相应的I/O对象分别为socket1, socket2, socket3, socket4,并且假设io_service现在正在处理socket1注册的handler。如果这个handler处理的过程很长,那么在这期间socket2,socket3,socket4注册的handler会一直得不到执行,造成不良的使用体验。

针对这个问题,解决之道只有采用多线程的方法。多线程的用法很简单,我们只要把线程函数boost::asio::io_service::run和io_service指针绑定好传给boost::thread类就好了。如下所示:

boost::thread t(boost::bind(&boost::asio::io_service::run, &io));t.join();

但是,引入多线程又会引入多线程同步的问题,如果这个问题没解决好,死机就是家常便饭了。幸好,asio给我们提供了strand这个类(当然,也可以使用mutex,但是使用strand会使代码更加优雅)。下面,简单介绍下strand这个类。

1) 用法

用法很简单,首先定义下变量。

boost::asio::io_service::strand strand_(&io); //注意io_service对象地址作为他的参数。

然后在注册回调函数时,在外面套上一层strand_.wrap()就好了,如下所示:

timer1_.async_wait(strand_.wrap(boost::bind(&printer::print1, this)));
timer2_.async_wait(strand_.wrap(boost::bind(&printer::print2, this)));
这样的话,这两个异步操作的回调函数肯定是被顺序执行的。

2) 源码分析

在分析源码之前,我们看下一个完整的调用堆栈:

我们把不采用strand时的调用堆栈图拿来比对下

不知道有没有被吓一跳,采用strand方式竟然会多出这么多层调用,让回调的路途看上去如此漫长。

好了,废话不多说,我们strand的那张调用堆栈图中寻找strand的蛛丝马迹。回调的路上这个函数boost::asio::io_service::strand::dispatch,顿时眼前一亮,让我想起strand类中的dispatch函数。眼尖的朋友可能发现调用堆栈上出现了两次boost::asio::io_service::strand::dispatch,不要奇怪,这两次的handler是不一样的,如下图。

这是先被调用的

这是后被调用的

下面贴出io_service::strand类:

class io_service::strand
{
public:explicit strand(boost::asio::io_service& io_service): service_(boost::asio::use_service<boost::asio::detail::strand_service>(io_service)){service_.construct(impl_);}~strand(){}boost::asio::io_service& get_io_service(){return service_.get_io_service();}/* 这就是第一个出现在回调路上的函数。这个函数的作用是让strand执行给定的handler。还有一点要说的就是,如果当前线程调用了service::run,那么该线程可以直接调用handler。这也是和post的区别之一。我们可以假想下如果回调的路上不是strand::dispatch,而是strand::post,那么我们的线程栈上也不一定会这么长了。*/template <typename CompletionHandler>BOOST_ASIO_INITFN_RESULT_TYPE(CompletionHandler, void ())dispatch(BOOST_ASIO_MOVE_ARG(CompletionHandler) handler){// If you get an error on the following line it means that your handler does// not meet the documented type requirements for a CompletionHandler.BOOST_ASIO_COMPLETION_HANDLER_CHECK(CompletionHandler, handler) type_check;detail::async_result_init<CompletionHandler, void ()> init(BOOST_ASIO_MOVE_CAST(CompletionHandler)(handler));// 注意此处是service_是boost::asio::detail::strand_service类型的哦;
// strand_service里面才是真正控制多线程安全的地方。service_.dispatch(impl_, init.handler);return init.result.get();}// 和dispatch都有投递任务的作用。只是post会马上返回,handler会被某个调用service::run的线程执行。template <typename CompletionHandler>BOOST_ASIO_INITFN_RESULT_TYPE(CompletionHandler, void ())post(BOOST_ASIO_MOVE_ARG(CompletionHandler) handler){// If you get an error on the following line it means that your handler does// not meet the documented type requirements for a CompletionHandler.BOOST_ASIO_COMPLETION_HANDLER_CHECK(CompletionHandler, handler) type_check;detail::async_result_init<CompletionHandler, void ()> init(BOOST_ASIO_MOVE_CAST(CompletionHandler)(handler));service_.post(impl_, init.handler);return init.result.get();}// 这个wrap函数就是上面说起的那个打包函数。有了它,io_service会先调用strand然后再调用handler,相当于在回调的路上设置了一道关卡,通过strand保证线程安全性。template <typename Handler>
#if defined(GENERATING_DOCUMENTATION)unspecified
#elsedetail::wrapped_handler<strand, Handler, detail::is_continuation_if_running>
#endifwrap(Handler handler){return detail::wrapped_handler<io_service::strand, Handler,detail::is_continuation_if_running>(*this, handler);}bool running_in_this_thread() const{return service_.running_in_this_thread(impl_);}private:boost::asio::detail::strand_service& service_;boost::asio::detail::strand_service::implementation_type impl_;
};

光这个类是看不出具体实现细节的,相要了解更多实现细节需要分析strand_service这个类。

具体的多线程控制方面,我们可以看下strand_service::strand_impl这个嵌套类

// The underlying implementation of a strand.class strand_impl: public operation{public:strand_impl();private:// Only this service will have access to the internal values.friend class strand_service;friend struct on_do_complete_exit;friend struct on_dispatch_exit;// 这就是那个用来多线程控制的互斥锁// Mutex to protect access to internal data.boost::asio::detail::mutex mutex_;// 用来表示strand是否被某个handler“锁住”的变量// Indicates whether the strand is currently "locked" by a handler. This// means that there is a handler upcall in progress, or that the strand// itself has been scheduled in order to invoke some pending handlers.bool locked_;// 哦,等待处理的排队队列// The handlers that are waiting on the strand but should not be run until// after the next time the strand is scheduled. This queue must only be// modified while the mutex is locked.op_queue<operation> waiting_queue_;// 已经获取锁并准备运行的handler// The handlers that are ready to be run. Logically speaking, these are the// handlers that hold the strand's lock. The ready queue is only modified// from within the strand and so may be accessed without locking the mutex.op_queue<operation> ready_queue_;};

可以看出mutex_、locked_、waiting_queue_、ready_queue_这四个变量保证了线程安全性。具体实现方法,可以自己去调试下,这里就不细细分析了(其实是肚子饿了,要去吃饭了^^)。

PS: 异步调用+多线程肯定会大大增加你的调试复杂度,加上日志记录是势在必行的事情。这里向大家推荐下简单易用的glog:https://github.com/google/glog

转载于:https://www.cnblogs.com/SudoSky/p/4523971.html

Boost.ASIO简要分析-4 多线程相关推荐

  1. java 线程局部存储,转载boost::thread简要分析(3):线程局部存储及其它

    多线程编程中还有一个重要的概念:Thread Local Store(TLS,线程局部存储),在boost中,TLS也被称作TSS,Thread Specific Storage. boost::th ...

  2. Boost asio学习笔记之一—— 使用strand支持多线程调用service_io的方法

    asio是一个跨平台的网络库,可以作为boost的一部分,也可以使用独立的asio部分.这里记录学习的笔记,作为参考. 感觉asio的关键就是io_service对象.所有的异步同步都跟这个有关.多线 ...

  3. boost asio io_service与 strand 分析

    1: io_service 与 strand 的关系是什么? 2: strand : /// Provides serialised handler execution. 能够保证线程安全,同时被po ...

  4. boost asio 应用方法学(二)——深入框架

    要用好它,就必须先了解它,而且不能停止于表面,必须深入到内部.而了解一件事物,先要了解它的框架,再了解它的细节.了解了框架,我们就有了提纲挈领的认识. 关于 boost asio 框架结构,在其文档中 ...

  5. Boost asio 官方教程简介

    1. 概述 本章介绍了 Boost C++ 库 Asio,它是异步输入输出的核心. 名字本身就说明了一切:Asio 意即异步输入/输出. 该库可以让 C++ 异步地处理数据,且平台独立. 异步数据处理 ...

  6. boost asio ——深入框架

    要用好它,就必须先了解它,而且不能停止于表面,必须深入到内部.而了解一件事物,先要了解它的框架,再了解它的细节.了解了框架,我们就有了提纲挈领的认识. 关于 boost asio 框架结构,在其文档中 ...

  7. Boost.Asio基本原理

    Boost.Asio基本原理 这一章涵盖了使用Boost.Asio时必须知道的一些事情.我们也将深入研究比同步编程更复杂.更有乐趣的异步编程. 网络API 这一部分包含了当使用Boost.Asio编写 ...

  8. Boost.Asio,libevent和ACE之间关于Socket编程的比较(★firecat推荐★)

    文章来源:http://blog.163.com/miky_sun/blog/static/3369405201041753652505/ ACE官网 http://download.dre.vand ...

  9. muduo 与 boost asio 吞吐量对比

    muduo (http://code.google.com/p/muduo) 是一个基于 Reactor 模式的 C++ 网络库,我在编写它的时候并没有以高并发高吞吐为主要目标,但出乎我的意料,pin ...

最新文章

  1. Java 基础知识 练习
  2. HTML、JSP、Servlet中的相对路径和绝对路径 页面跳转问题
  3. js动态添加meta标签
  4. 异常处理—错误抛出机制
  5. AlvinZH双掉坑里了
  6. android浮动按钮_Android浮动操作按钮示例教程
  7. [linux] redhat 7 ssh 安装配置免密登录
  8. Mac很好用的音乐转换器:NoteBurner Spotify Music Converter mac
  9. HTTP请求方式和幂等性
  10. 熬了多少个夜晚,大家期待的《网络工程师思科华为华三实战案例红宝书》即网工必备技术命令大全版本1完书...
  11. JavaScript函数(二)回调函数
  12. 九个最佳ICON图标搜索引擎
  13. SpringCloud之熔断器Hystrix
  14. als算法参数_矩阵分解之交替最小二乘ALS
  15. C语言函数之可变参数原理:va_start、va_arg及va_end
  16. 2019年Robomaster江苏省赛总结
  17. python怎样分析文献综述_教你如何做文献综述
  18. python控制qq群_Python3 selenium 实现QQ群接龙自动化功能
  19. Javascript中undefined和not defined有什么区别?
  20. 云计算技术-01-云计算与云服务概述

热门文章

  1. ActiveMQ 权限
  2. Flask 区域块简单原理实现
  3. China’s movie heroes 《红海行动》展现中国英雄本色
  4. Java数据类型分类
  5. LVS学习笔记--DR模式部署
  6. nginx的反向代理、负载均衡、页面缓存、URL重写及读写分离
  7. Java实现的基于socket的一次通信
  8. 《Java程序员,上班那点事儿》 - 书摘精要
  9. Windows Server 2008 R2 Network Recovery
  10. java读取配置文件