[英]Efficiently listening on multiple sockets even when file descriptors are not available
假设我有两个库(A 和 B),每个库都有一个 function 监听 sockets。这些函数使用 select(),如果数据到达,它们会立即返回一些事件,否则它们会等待一段时间(超时)然后返回 NULL:
A_event_t* A_wait_for_event(int timeout);
B_event_t* B_wait_for_event(int timeout);
现在,我在我的程序中使用它们:
int main (int argc, char *argv[]) {
// Init A
// Init B
// .. do some other initialization
A_event_t *evA;
B_event_t *evB;
for(;;) {
evA = A_wait_for_event(50);
evB = B_wait_for_event(50);
// do some work based on events
}
}
每个库都有自己的 sockets(例如 udp 套接字)并且无法从外部访问。
问题:这不是很有效。 例如,如果有很多事件等待 *B_wait_for_event* 传递,这些事件将不得不一直等待直到 *A_wait_for_event* 超时,这有效地限制了库 B 和我的程序的吞吐量。
通常,可以使用线程来分离处理,但是如果某些事件的处理需要调用其他库的 function 怎么办,反之亦然。 例子:
if (evA != 0 && evA == A_EVENT_1) {
B_do_something();
}
if (evB != 0 && evB == B_EVENT_C) {
A_do_something();
}
因此,即使我可以创建两个线程并将功能与库分开,这些线程也必须在它们之间交换事件(可能通过管道)。 这仍然会限制性能,因为一个线程会被 *X_wait_for_event()* function 阻塞,并且不可能立即从其他线程接收数据。
如何解决这个问题?
根据您使用的库,此解决方案可能不可用,但最好的解决方案是不要调用等待事件的各个库中的函数。 每个库都应该支持连接到外部事件循环。 然后您的应用程序使用一个循环,其中包含一个poll()
或select()
调用,该循环等待您使用的所有库想要等待的所有事件。
glib 的事件循环对此很有帮助,因为许多库已经知道如何挂钩它。 但是如果你不使用像 glib 这样复杂的东西,通常的方法是这样的:
poll()
poll()
返回时可能发生的任何事件。是的,早期的图书馆仍然有可能让后来的图书馆饿死,但它在实践中是有效的。
如果您使用的库不支持这种设置和调度接口,请将其添加为功能并向上游贡献代码!
(我将其移至答案,因为评论时间太长了)
如果您处于不允许在一个线程中调用A_do_something
而另一个线程正在执行A_wait_for_event
(对于B
也是如此)的情况,那么我很确定您无法做任何有效的事情,并且必须在两者之间解决各种邪恶。
最明显的改进是在收到事件后立即采取行动,而不是尝试从两者中读取:即订购您的循环
您可以做的其他缓解措施是
编辑:您可以检查您的图书馆的 API; 他们可能已经提供了解决问题的方法。 例如,它们可能允许您为事件注册回调,并通过回调获取事件通知,而不是轮询wait_for_event
。
另一件事是你是否可以为库创建新的文件描述符来监听。 例如,如果您创建一个新的 pipe 并将一端交给库A
,那么如果线程 #1 正在等待A
事件,线程 #2 可以写入 pipe 以使事件发生,从而迫使 #1 退出wait_for_event
。 由于能够随意将线程踢出wait_for_event
函数,各种新选项都变得可用。
一个可能的解决方案是在“做某事”的“主”线程中使用两个线程来wait_for_events
加上boost::condition_variable
。 一个相似但不精确的解决方案在这里
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.