1

假设我有两个库(A 和 B),每个库都有一个侦听套接字的函数。这些函数使用 select() ,如果数据到达,它们会立即返回一些事件,否则它们会等待一段时间(timeout)然后返回 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()* 函数阻塞,并且不可能立即从其他线程接收数据。

如何解决这个问题?

4

3 回答 3

3

根据您使用的库,此解决方案可能不可用,但最好的解决方案是不要调用单个库中等待事件的函数。每个库都应该支持挂钩到外部事件循环。然后,您的应用程序使用一个循环,其中包含一个poll()orselect()调用,该循环等待您使用的所有库想要等待的所有事件。

glib 的事件循环对此很有用,因为许多库已经知道如何挂钩。但是如果你不使用像 glib 这样复杂的东西,通常的方法是这样的:

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

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

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

于 2012-04-04T14:38:41.187 回答
2

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

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

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

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

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

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

编辑:您可以检查库的 API;他们可能已经提供了解决问题的方法。例如,它们可能允许您为事件注册回调,并通过回调而不是轮询获取事件通知wait_for_event

另一件事是您是否可以为库创建新的文件描述符以进行侦听。例如,如果您创建一个新管道并将一端交给 library A,那么如果线程 #1 正在等待A事件,线程 #2 可以写入管道以使事件发生,从而迫使 #1 退出wait_for_event。由于能够随意将线程踢出wait_for_event函数,因此可以使用各种新选项。

于 2012-04-04T14:22:37.487 回答
1

一个可能的解决方案是使用两个线程来wait_for_events加上boost::condition_variable“做某事”的“主”线程。一个相似但不精确的解决方案在这里

于 2012-04-04T14:21:23.083 回答