即使文件描述符不可用,也可以有效地监听多个套接字

时间:2012-04-04 13:58:47

标签: c++ c linux sockets

假设我有两个库(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()*函数会阻塞一个线程,并且无法立即从其他线程接收数据。

如何解决这个问题?

3 个答案:

答案 0 :(得分:3)

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

glib's event loop对此有好处,因为许多库已经知道如何挂钩它。但是如果你不使用像glib那样复杂的东西,通常的做法就是:

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

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

如果您使用的库不支持此类设置&调度接口,将其添加为功能并在上游提供代码!

答案 1 :(得分:2)

(我正在回答这个问题,因为评论的时间太长了)

如果您处于这样的情况:当另一个线程正在执行时A_do_something(同样对于A_wait_for_event),您不允许在一个线程中调用B,那么我就是很确定你不能做任何有效的事情,并且必须在各种邪恶之间解决。

最明显的改进是立即采取行动,而不是试图从两者中读取:即命令你的循环

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

你可以做的其他缓解措施

  • 尝试预测A事件或B事件是否更有可能接下来,并等待第一个事件。 (例如,如果他们有条纹,那么在获得和处理A事件后,你应该回去等待另一个A事件)
  • 摆弄超时值以在自旋循环和过多阻塞之间取得平衡。 (甚至可能动态调整)

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

另一件事是,如果您可以为要监听的库创建新的文件描述符。例如如果您创建一个新管道并将一端交给库A,那么如果线程#1正在等待A事件,则线程#2可以写入管道以使事件发生,从而强制执行wait_for_event中排名第一。能够随意将线程踢出wait_for_event函数,可以使用各种新选项。

答案 2 :(得分:1)

一种可能的解决方案是在“主”线程中使用两个线程到wait_for_eventsboost::condition_variable“做某事”。 here

是一个相似但不完全正确的解决方案