我有一个工作线程正在侦听TCP套接字以获取传入流量,并缓冲接收到的主线程要访问的数据(让我们称之为套接字A).然而,工作线程也有做一些常规操作(比如,每秒一次),即使没有数据进来,所以我用select()
了超时,让我不需要保持轮询.(请注意,调用receive()
在非阻塞套接字,然后睡一秒钟也不好:输入数据应该是立即可用于主线程,即使主线程可能并不总是能够处理它的时候了,因此需要缓冲.)
现在,我还需要能够发信号通知工作线程立即做其他事情; 从主线程,我需要立即让工作线程select()
返回.现在,我已经解决了这个问题(方法基本上从这里和这里采用):
在程序启动时,工作线程用于此目的创建数据报(UDP)类型的附加插座,并将其绑定到一些随机端口(我们称之为插座乙).同样,主线程创建一个用于发送的数据报套接字.在其号召select()
,工作线程,将列出一个和乙在fd_set
.当主线程需要发信号时,它sendto()
是相应端口的几个字节localhost
.回到工作线程中,如果B保留在fd_set
after select()
返回中,则recvfrom()
调用并且接收的字节被简单地忽略.
这似乎工作得很好,但我不能说我喜欢这个解决方案,主要是因为它需要为B绑定一个额外的端口,还因为它添加了几个额外的套接字API调用,这可能会失败我猜 - 我不知道真的想要找出每个案件的适当行动.
我认为理想情况下,我想调用一些以A作为输入的函数,除了立即select()
返回之外什么都不做.但是,我不知道这样的功能.(我想我可以举例来说shutdown()
套接字,但副作用并不是真的可以接受:)
如果这是不可能的,那么第二个最佳选择是创建一个比真正的UDP套接字更糟糕的B,并且实际上并不需要分配任何有限的资源(超出合理的内存量).我想Unix域套接字就可以做到这一点,但是:解决方案不应该比现在的解决方案少得多,尽管一些适量的#ifdef
东西都可以.(我主要针对Windows和Linux - 并且顺便编写C++.)
请不要建议重构以摆脱两个单独的线程.这种设计是必要的,因为主线程可能会被长时间阻塞(例如,做一些密集的计算 - 我无法receive()
从最里面的计算循环开始定期调用),同时,有人需要缓冲传入的数据(由于我无法控制的原因,它不能是发件人).
现在我正在写这篇文章,我意识到有人肯定会简单地回复" Boost.Asio ",所以我刚刚看了它......但是找不到明显的解决方案.请注意,我也不能(轻松地)影响套接字A的创建方式,但如果需要,我应该可以让其他对象包装它.
你快到了.使用"自我管道"技巧.打开一个管道,将其添加到您的select()
读取和写入fd_set
,从主线程写入它以取消阻止工作线程.它可以跨POSIX系统移植.
我在一个系统中看到了Windows的类似技术的变体(实际上与上面的方法一起使用,分开#ifdef WIN32
).可以通过向其添加虚拟(未绑定)数据报套接字fd_set
然后将其关闭来实现解除阻塞.缺点是,当然,你必须每次都重新打开它.
然而,在前述系统中,这两种方法都被相当谨慎地使用,并且用于意外事件(例如,信号,终止请求).首选方法仍然是变量超时select()
,具体取决于为工作线程调度的时间.