【发布时间】:2010-09-27 22:00:02
【问题描述】:
我有一个工作线程正在侦听 TCP 套接字以获取传入流量,并缓冲接收到的数据以供主线程访问(我们将此套接字称为 A)。但是,工作线程也必须做一些常规操作(比如每秒一次),即使没有数据进来。因此,我使用带有超时的select(),这样我就不需要继续轮询. (请注意,在非阻塞套接字上调用receive() 然后休眠一秒钟是不好的:传入的数据应该立即可用于主线程,即使主线程可能并不总是能够立即处理它,因此需要缓冲。)
现在,我还需要能够立即向工作线程发出信号以执行其他操作;从主线程,我需要让工作线程的select() 立即返回。目前,我已经解决了这个问题(基本上采用here和here的方法):
在程序启动时,工作线程为此目的创建一个额外的数据报 (UDP) 类型的套接字,并将其绑定到某个随机端口(我们称之为套接字B)。同样,主线程创建一个用于发送的数据报套接字。在对select() 的调用中,工作线程现在在fd_set 中列出了A 和B。当主线程需要发信号时,它sendto() 的几个字节到localhost 上的相应端口。回到工作线程,如果在select() 返回后B 仍留在fd_set 中,则调用recvfrom() 并简单地忽略接收到的字节。
这似乎工作得很好,但我不能说我喜欢这个解决方案,主要是因为它需要为 B 绑定一个额外的端口,还因为它添加了几个额外的套接字 API 调用我猜可能会失败——而且我真的不想为每种情况找出适当的行动。
我认为理想情况下,我想调用一些以 A 作为输入的函数,除了让select() 立即返回之外什么都不做。但是,我不知道这样的功能。 (我想我可以使用例如shutdown() 套接字,但副作用是不能接受的:)
如果这不可能,第二好的选择是创建一个 B,它比真正的 UDP 套接字更笨,并且不需要分配任何有限的资源(超出合理的数量)内存)。我猜Unix domain sockets 会做到这一点,但是:该解决方案不应该比我目前拥有的跨平台少得多,尽管一些适量的#ifdef 东西很好。 (我主要针对 Windows 和 Linux ——顺便写一下 C++。)
请不要建议重构以摆脱两个单独的线程。这种设计是必要的,因为主线程可能会被阻塞很长时间(例如,做一些密集的计算——我不能从最里面的计算循环开始定期调用receive()),同时,有人需要缓冲传入的数据(由于我无法控制的原因,它不能是发送者)。
现在我正在写这篇文章,我意识到肯定有人会简单地回复“Boost.Asio”,所以我只是第一次看到它......但找不到明显的解决方案。请注意,我也不能(轻松)影响套接字 A 的创建方式,但如果需要,我应该能够让其他对象包装它。
【问题讨论】:
标签: c multithreading select sockets