繁体   English   中英

即使文件描述符不可用,也能有效地监听多个 sockets

[英]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 这样复杂的东西,通常的方法是这样的:

  • 永远循环:
    • 从一个无限计时器和一组空的文件描述符开始
    • 对于您使用的每个库:
      • 在库中调用设置 function 允许将文件描述符添加到您的集合和/或缩短(但不能延长)超时。
    • 运行poll()
    • 对于您使用的每个库:
      • 在库中调用调度 function 以响应poll()返回时可能发生的任何事件。

是的,早期的图书馆仍然有可能让后来的图书馆饿死,但它在实践中是有效的。

如果您使用的库不支持这种设置和调度接口,请将其添加为功能并向上游贡献代码!

(我将其移至答案,因为评论时间太长了)

如果您处于不允许在一个线程中调用A_do_something而另一个线程正在执行A_wait_for_event (对于B也是如此)的情况,那么我很确定您无法做任何有效的事情,并且必须在两者之间解决各种邪恶。

最明显的改进是在收到事件后立即采取行动,而不是尝试从两者中读取:即订购您的循环

  • 等待 A 事件
  • 也许在 B 做点什么
  • 等待 B 事件
  • 也许在A做点什么

您可以做的其他缓解措施是

  • 尝试预测接下来发生的是 A 事件还是 B 事件,然后先等待。 (例如,如果它们连续出现,那么在获取并处理 A 事件后,您应该 go 返回等待另一个 A 事件)
  • 摆弄超时值以在自旋循环和过多阻塞之间取得平衡。 (甚至可以动态调整)

编辑:您可以检查您的图书馆的 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM