转自:http://www.zhihu.com/question/39752285/answer/82906915
linux多线程网络编程中有一段话:
当然,pipe也有一个经典应用场景,那就是写Reactor/event loop 时用来唤醒异步select调用?网上没找到具体的应用场景,不知道是怎么唤醒异步调用


请搜索:self pipe trick。
reactor展开了写就是个等待和分发事件的过程:
events = selector.wait(milliseconds)
for fd, event in events:
    if event & EVT_READ:
       handle_read(fd)
    if event & EVT_WRITE:
       handle_write(fd)

问题出在wait上,没新消息它将一直等到milliseconds指定的时间为准,而此时ui上用户说了句话,点击“发送”,ui线程把待发送的内容推到了网络线程的消息队列里,而网络线程还在wait呢,没网络事件的话,只有等待这轮wait结束网络线程才有空到队列里监测并处理刚才ui线程投递过来的待发送消息。

select等待时间过长将会让消息不能即时被处理,而过短又会占用过多cpu费电,因此在想能不能平时wait长一点,而当我ui线程刚点击了发送按钮就立即把网络线程从select的wait中唤醒让网络线程可以即时的查看自己的消息队列就方便了。

于是大家把管道的读取端fd放入selector,那么在wait的时候这个读取端管道fd也会一起参与wait,那么ui线程往队列里塞完任务后,马上往管道的写端写入一个字节,就可以把网络线程唤醒了。

这个方法是用来解决多个reactor之间互相唤醒的问题的,利用该技巧可以让网络线程即时处理网络事件的同时也能即时处理来自非网络(比如内部消息队列)的其它消息。

就是所谓的self pipe trick,说白了也很简单,windows下select只能针对socket套接字,不能针对管道,一般用构造两个互相链接于localhost的socket来模拟之。不过win下select最多支持同时wait 64个套接字,你摸拟的pipe占掉一个,就只剩下63个可用了。

所以java的nio里selector在windows下最多支持62个套接字就是被self pipe trick占掉了两个,一个用于其它线程调用notify唤醒,另一个留作jre内部保留,就是这个原因。

说白了这其实就是个年代久远的系统层api设计考虑不周全,要应用层来给它打补丁的典型例子。

倘若系统层直接支持这样的唤醒,就不用应用层构造什么管道了。
Logo

更多推荐