即使在文件描述符不可用的情况下,也能有效地侦听多个套接字
Efficiently listening on multiple sockets even when file descriptors are not available
假设我有两个库(A和B),每个库都有一个监听套接字的函数。这些函数使用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
}
}
每个库都有自己的套接字(例如udp套接字),并且不能从外部访问。
问题:这不是很有效。例如,如果有很多事件等待*B_wait_for_event*传递,那么这些事件将不得不一直等到*a_wait_for-event*超时,这实际上限制了库B和我的程序的吞吐量。
通常,可以使用线程来分离处理,但如果某个事件的处理需要调用其他库的函数,反之亦然。示例:
if (evA != 0 && evA == A_EVENT_1) {
B_do_something();
}
if (evB != 0 && evB == B_EVENT_C) {
A_do_something();
}
因此,即使我可以创建两个线程并将功能从库中分离出来,这些线程也必须在它们之间交换事件(可能通过管道)。这仍然会限制性能,因为一个线程会被*X_wait_for_event()*函数阻塞,并且不可能立即从其他线程接收数据。
如何解决这个问题?
此解决方案可能不可用,具体取决于您使用的库,但最好的解决方案是而不是调用单个库中等待事件的函数。每个库都应该支持挂接到外部事件循环中。然后,您的应用程序使用一个包含poll()
或select()
调用的循环,该调用将等待您使用的所有库要等待的所有事件。
glib的事件循环对此很好,因为许多库已经知道如何连接到它。但如果你不使用像glib这样复杂的东西,正常的方法是:
- 永远循环:
- 从一个无限计时器和一组空的文件描述符开始
- 对于您使用的每个库:
- 调用库中的设置函数,该函数可以将文件描述符添加到您的集合和/或缩短(但不能延长)超时时间
- 运行
poll()
- 对于您使用的每个库:
- 调用库中的调度函数,该函数将响应
poll()
返回时可能发生的任何事件
- 调用库中的调度函数,该函数将响应
是的,早期的图书馆仍然有可能饿死后期的图书馆,但这在实践中是有效的。
如果你使用的库不支持这种设置&dispatch接口,将其作为一个特性添加,并向上游贡献代码!
如果您在一个线程中不允许调用A_do_something
,而另一个线程正在执行A_wait_for_event
(类似于B
),那么我敢肯定,您不能做任何有效的事情,必须在各种邪恶之间解决。
最明显的改进是在收到事件后立即采取行动,而不是试图从两者中读取:即订购循环
- 等待A事件
- 也许在B做点什么
- 等待B事件
- 也许在A做点什么
你可以做的其他缓解措施是
- 试着预测A事件还是B事件更有可能发生,然后先等待。(例如,如果他们连续出现,那么在获得并处理了一个A事件后,你应该回去等待另一个A活动)
- 摆弄暂停值,在旋转循环和过多的阻挡之间取得平衡。(甚至可能动态调整)
编辑:您可以检查库的API;他们可能已经提供了一种解决问题的方法。例如,它们可能允许您注册事件的回调,并通过回调获取事件通知,而不是轮询wait_for_event
。
另一件事是,如果你可以为库创建新的文件描述符来侦听。例如,如果你创建了一个新的管道,并将一端交给库A
,那么如果线程#1正在等待A
事件,线程#2可以写入管道以使事件发生,从而迫使#1退出wait_for_event
。有了随意将线程踢出wait_for_event
函数的能力,各种各样的新选项都变得可用。
一个可能的解决方案是在"执行某些操作"的"main"线程中使用两个线程来处理wait_for_events
和boost::condition_variable
。这里有一个相似但不精确的解决方案
- 当套接字连接断开时检测C/C++Unix
- 通过套接字[TCP]传输数据 如何在C / C ++中打包多个整数并使用send() recv()传输数据
- 如何通过套接字将文本文件的内容从服务器发送到客户端
- 如何在C/C++中用FD_set Unix设置套接字文件描述符
- 套接字读取后,我在缓冲区中看到意外输入
- 如何在CPP中创建应该在Windows和Linux上运行的套接字?
- 我可以与 python 服务器而不是 c++ 客户端建立 tcp/ip 套接字吗?
- 套接字连接"Operation not permitted"错误,甚至使用升压/平发器根.cpp
- WINAPI 注册应用程序重新启动时不清除打开的套接字
- (Winsock) UDP 接收工作正常,但同一套接字的发送失败
- 在UDP套接字上多次调用Connect()以发送到不同的目标地址 - 有效
- 在多线程HTTP服务器中发送后,如何干净地关闭套接字
- 通过套接字一致地丢失数据(但在使用本地主机连接时不会)
- 通过套接字发送二进制文件.文本文件有效,其他文件无效
- 处理客户端连接的最有效方法(套接字编程)
- 如何在通过套接字发送多个图像之前正确地对其进行定界
- 即使在文件描述符不可用的情况下,也能有效地侦听多个套接字
- 我如何有效地使用boost asio套接字进行全双工流
- C 套接字/轮询错误地返回 POLLOUT 事件
- 优雅地关闭TCP套接字