如何通知 select() 立即返回? [英] How to signal select() to return immediately?

查看:20
本文介绍了如何通知 select() 立即返回?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个工作线程,它正在侦听 TCP 套接字的传入流量,并缓冲接收到的数据以供主线程访问(我们将此套接字称为 A).但是,即使没有数据传入,工作线程也必须执行一些常规操作(例如,每秒一次).因此,我使用带有超时的 select(),以便我不需要继续投票.(请注意,在非阻塞套接字上调用 receive() 然后休眠一秒钟是不好的:传入的数据应该立即可供主线程使用,即使主线程可能并不总是能够立即处理它,因此需要缓冲.)

I have a worker thread that is listening to a TCP socket for incoming traffic, and buffering the received data for the main thread to access (let's call this socket A). However, the worker thread also has to do some regular operations (say, once per second), even if there is no data coming in. Therefore, I use select() with a timeout, so that I don't need to keep polling. (Note that calling receive() on a non-blocking socket and then sleeping for a second is not good: the incoming data should be immediately available for the main thread, even though the main thread might not always be able to process it right away, hence the need for buffering.)

现在,我还需要能够通知工作线程立即执行其他一些操作;从主线程,我需要让工作线程的 select() 立即返回.现在,我已经解决了这个问题(方法基本上从 here此处):

Now, I also need to be able to signal the worker thread to do some other stuff immediately; from the main thread, I need to make the worker thread's select() return right away. For now, I have solved this as follows (approach basically adopted from here and here):

在程序启动时,工作线程为此创建了一个额外的数据报 (UDP) 类型的套接字,并将其绑定到某个随机端口(我们称之为套接字 B).同样,主线程创建一个数据报套接字用于发送.在对 select() 的调用中,工作线程现在在 fd_set 中列出了 AB.当主线程需要发信号时,它sendto()localhost 上的相应端口发送几个字节.回到工作线程,如果 select() 返回后 B 仍然在 fd_set 中,则 recvfrom()被调用并且接收到的字节将被简单地忽略.

At program startup, the worker thread creates for this purpose an additional socket of the datagram (UDP) type, and binds it to some random port (let's call this socket B). Likewise, the main thread creates a datagram socket for sending. In its call to select(), the worker thread now lists both A and B in the fd_set. When the main thread needs to signal, it sendto()'s a couple of bytes to the corresponding port on localhost. Back in the worker thread, if B remains in the fd_set after select() returns, then recvfrom() is called and the bytes received are simply ignored.

这似乎工作得很好,但我不能说我喜欢这个解决方案,主要是因为它需要为 B 绑定一个额外的端口,还因为它添加了几个额外的套接字 API 调用,这些调用我猜可能会失败——而且我真的不想为每个案例找出适当的操作.

This seems to work very well, but I can't say I like the solution, mainly as it requires binding an extra port for B, and also because it adds several additional socket API calls which may fail I guess – and I don't really feel like figuring out the appropriate action for each of the cases.

我认为理想情况下,我想调用一些将 A 作为输入的函数,除了让 select() 立即返回外,什么都不做.但是,我不知道这样的功能.(我想我可以例如 shutdown() 套接字,但副作用不是真的可以接受:)

I think ideally, I would like to call some function which takes A as input, and does nothing except makes select() return right away. However, I don't know such a function. (I guess I could for example shutdown() the socket, but the side effects are not really acceptable :)

如果这是不可能的,第二个最好的选择是创建一个 B,它比真正的 UDP 套接字要傻得多,并且实际上不需要分配任何有限的资源(超出合理数量记忆).我猜 Unix 域套接字 会做到这一点,但是:解决方案不应该更少交叉平台比我目前拥有的要好,尽管适量的 #ifdef 东西很好.(我主要针对 Windows 和 Linux - 顺便写 C++.)

If this is not possible, the second best option would be creating a B which is much dummier than a real UDP socket, and doesn't really require allocating any limited resources (beyond a reasonable amount of memory). I guess Unix domain sockets would do exactly this, but: the solution should not be much less cross-platform than what I currently have, though some moderate amount of #ifdef stuff is fine. (I am targeting mainly for Windows and Linux – and writing C++ by the way.)

请不要建议重构以摆脱两个单独的线程.这种设计是必要的,因为主线程可能会被阻塞很长时间(例如,进行一些密集的计算 - 我无法从最内层的计算循环开始定期调用 receive()),并且在同时,有人需要缓冲传入的数据(由于我无法控制的原因,它不能是发送方).

Please don't suggest refactoring to get rid of the two separate threads. This design is necessary because the main thread may be blocked for extended periods (e.g., doing some intensive computation – and I can't start periodically calling receive() from the innermost loop of calculation), and in the meanwhile, someone needs to buffer the incoming data (and due to reasons beyond what I can control, it cannot be the sender).

写到这里,我意识到肯定有人会简单地回复Boost.Asio",所以我只是第一次看到它......但找不到明显的解决方案.请注意,我也不能(轻松地)影响套接字 A 的创建方式,但如有必要,我应该能够让其他对象包装它.

Now that I was writing this, I realized that someone is definitely going to reply simply "Boost.Asio", so I just had my first look at it... Couldn't find an obvious solution, though. Do note that I also cannot (easily) affect how socket A is created, but I should be able to let other objects wrap it, if necessary.

推荐答案

您就快到了.使用自管"技巧.打开一个管道,将其添加到您的 select() 读写 fd_set,从主线程写入以解除对工作线程的阻塞.它可以跨 POSIX 系统移植.

You are almost there. Use a "self-pipe" trick. Open a pipe, add it to your select() read and write fd_set, write to it from main thread to unblock a worker thread. It is portable across POSIX systems.

我曾在一个系统中看到过类似的 Windows 技术的变体(实际上与上述方法一起使用,由 #ifdef WIN32 分隔).解除阻塞可以通过向 fd_set 添加一个虚拟(未绑定)数据报套接字然后关闭它来实现.缺点是,当然,您每次都必须重新打开它.

I have seen a variant of similar technique for Windows in one system (in fact used together with the method above, separated by #ifdef WIN32). Unblocking can be achieved by adding a dummy (unbound) datagram socket to fd_set and then closing it. The downside is that, of course, you have to re-open it every time.

然而,在上述系统中,这两种方法都被相当谨慎地使用,并且用于意外事件(例如,信号、终止请求).首选方法仍然是 select() 的可变超时,具体取决于为工作线程安排的时间.

However, in the aforementioned system, both of these methods are used rather sparingly, and for unexpected events (e.g., signals, termination requests). Preferred method is still a variable timeout to select(), depending on how soon something is scheduled for a worker thread.

这篇关于如何通知 select() 立即返回?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆