12.13 多个线程队列轮询

    对于轮询问题的一个常见解决方案中有个很少有人知道的技巧,包含了一个隐藏的回路网络连接。本质上讲其思想就是:对于每个你想要轮询的队列,你创建一对连接的套接字。然后你在其中一个套接字上面编写代码来标识存在的数据,另外一个套接字被传给 或类似的一个轮询数据到达的函数。下面的例子演示了这个思想:

    在这个代码中,一个新的 Queue 实例类型被定义,底层是一个被连接套接字对。在Unix机器上的 socketpair() 函数能轻松的创建这样的套接字。在Windows上面,你必须使用类似代码来模拟它。然后定义普通的 和 put() 方法在这些套接字上面来执行I/O操作。put() 方法再将数据放入队列后会写一个单字节到某个套接字中去。而 方法在从队列中移除一个元素时会从另外一个套接字中读取到这个单字节数据。

    下面是一个例子,定义了一个为到来的元素监控多个队列的消费者:

    如果你试着运行它,你会发现这个消费者会接受到所有的被放入的元素,不管元素被放进了哪个队列中。

    这样做其实不合理,还会引入其他的性能问题。例如,如果新的数据被加入到一个队列中,至少要花10毫秒才能被发现。如果你之前的轮询还要去轮询其他对象,比如网络套接字那还会有更多问题。例如,如果你想同时轮询套接字和队列,你可能要像下面这样使用:

    这个方案通过将队列和套接字等同对待来解决了大部分的问题。一个单独的 select() 调用可被同时用来轮询。使用超时或其他基于时间的机制来执行周期性检查并没有必要。甚至,如果数据被加入到一个队列,消费者几乎可以实时的被通知。尽管会有一点点底层的I/O损耗,使用它通常会获得更好的响应时间并简化编程。