Boost.ASIO简要分析-4多线程

更新时间:2023-05-21 16:47:57 阅读: 评论:0

Boost.ASIO简要分析-4多线程
4. 多线程
⼀般情况下,服务端开启⼀条线程做io_rvice::run()⼯作就⾜够了。但是,有些情况下可能会变得很糟糕。
从之前的分析,我们知道异步操作的⼀个关键步骤就是io_rvice回调我们注册的handler。现在假设客户端与服务端建⽴了四个socket连接,相应的I/O对象分别为socket1, socket2, socket3, socket4,并且假设io_rvice现在正在处理socket1注册的handler。如果这个handler处理的过程很长,那么在这期间socket2,socket3,socket4注册的handler会⼀直得不到执⾏,造成不良的使⽤体验。
针对这个问题,解决之道只有采⽤多线程的⽅法。多线程的⽤法很简单,我们只要把线程函数boost::asio::io_rvice::run和io_rvice指针绑定好传给boost::thread类就好了。如下所⽰:
boost::thread t(boost::bind(&boost::asio::io_rvice::run, &io));
简爱在线阅读t.join();
但是,引⼊多线程⼜会引⼊多线程同步的问题,如果这个问题没解决好,死机就是家常便饭了。幸好,a
sio给我们提供了strand这个类(当然,也可以使⽤mutex,但是使⽤strand会使代码更加优雅)。下⾯,简单介绍下strand这个类。
1) ⽤法
⽤法很简单,⾸先定义下变量。
boost::asio::io_rvice::strand strand_(&io); //注意io_rvice对象地址作为他的参数。
然后在注册回调函数时,在外⾯套上⼀层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_rvice::strand::dispatch,顿时眼前⼀亮,让我想起strand类中的dispatch函数。眼尖的朋友可能发现调⽤堆栈上出现了两次boost::asio::io_rvice::strand::dispatch,不要奇怪,这两次的handler是不⼀样的,如下图。
这是先被调⽤的
merrell
这是后被调⽤的
下⾯贴出io_rvice::strand类:
class io_rvice::strand
{
public:
explicit strand(boost::asio::io_rvice& io_rvice)
: rvice_(boost::asio::u_rvice<
boost::asio::detail::strand_rvice>(io_rvice))
{
{
rvice_.construct(impl_);
}
~strand()
{
}
boost::asio::io_rvice& get_io_rvice()
{
return rvice_.get_io_rvice();
}
/* 这就是第⼀个出现在回调路上的函数。
这个函数的作⽤是让strand执⾏给定的handler。
还有⼀点要说的就是,如果当前线程调⽤了rvice::run,那么该线程可以直接调⽤handler。这也是和post的区别之⼀。我们可以假想下如果回调的路上不是strand:  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));
// 注意此处是rvice_是boost::asio::detail::strand_rvice类型的哦;
// strand_rvice⾥⾯才是真正控制多线程安全的地⽅。
joy2keyrvice_.dispatch(impl_, init.handler);
();
}happy thanksgiving
// 和dispatch都有投递任务的作⽤。只是post会马上返回,handler会被某个调⽤rvice::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));
rvice_.post(impl_, init.handler);
();
疯狂舞台}
// 这个wrap函数就是上⾯说起的那个打包函数。有了它,io_rvice会先调⽤strand然后再调⽤handler,相当于在回调的路上设置了⼀道关卡,通过strand保证线程安  template <typename Handler>
#if defined(GENERATING_DOCUMENTATION)
unspecified
#el
detail::wrapped_handler<strand, Handler, detail::is_continuation_if_running>
#endif
wrap(Handler handler)
{
return detail::wrapped_handler<io_rvice::strand, Handler,
detail::is_continuation_if_running>(*this, handler);
}
bool running_in_this_thread() const
{
return rvice_.running_in_this_thread(impl_);
}
private:
boost::asio::detail::strand_rvice& rvice_;
boost::asio::detail::strand_rvice::implementation_type impl_;
};
光这个类是看不出具体实现细节的,相要了解更多实现细节需要分析strand_rvice这个类。具体的多线程控制⽅⾯,我们可以看下strand_rvice::strand_impl这个嵌套类
// The underlying implementation of a strand.
class strand_impltq
: public operation
{
public:fdm
strand_impl();
private:
// Only this rvice will have access to the internal values.
friend class strand_rvice;
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
// itlf 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, the are thepa是什么意思
// handlers that hold the strand's lock. The ready queue is only modified
// from within the strand and so may be accesd without locking the mutex.
op_queue<operation> ready_queue_;
};
可以看出mutex_、locked_、waiting_queue_、ready_queue_这四个变量保证了线程安全性。具体实现⽅法,可以⾃⼰去调试下,这⾥就不细细分析了(其实是肚⼦饿了,要去吃饭了^^)。

本文发布于:2023-05-21 16:47:57,感谢您对本站的认可!

本文链接:https://www.wtabcd.cn/fanwen/fan/90/117304.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:回调   函数   线程   问题   细节
相关文章
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图