简体   繁体   English

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

[英]Efficiently listening on multiple sockets even when file descriptors are not available

Let's say that I have two libraries (A and B), and each has one function that listen on sockets. These functions use select() and they return some event immediately if the data has arrived, otherwise they wait for some time ( timeout ) and then return NULL:假设我有两个库(A 和 B),每个库都有一个 function 监听 sockets。这些函数使用 select(),如果数据到达,它们会立即返回一些事件,否则它们会等待一段时间(超时)然后返回 NULL:

A_event_t* A_wait_for_event(int timeout);
B_event_t* B_wait_for_event(int timeout); 

Now, I use them in my program:现在,我在我的程序中使用它们:

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
    }
}

Each library has its own sockets (eg udp socket) and it is not accessible from outside.每个库都有自己的 sockets(例如 udp 套接字)并且无法从外部访问。

PROBLEM : This is not very efficient.问题:这不是很有效。 If for example there is a lot of events waiting to be delivered by *B_wait_for_event* these would have to wait always until *A_wait_for_event* timeouts, which effectively limits the throughput of library B and my program.例如,如果有很多事件等待 *B_wait_for_event* 传递,这些事件将不得不一直等待直到 *A_wait_for_event* 超时,这有效地限制了库 B 和我的程序的吞吐量。

Normally, one could use threads to separate processing, BUT what if processing of some event require to call function of other library and vice verse.通常,可以使用线程来分离处理,但是如果某些事件的处理需要调用其他库的 function 怎么办,反之亦然。 Example:例子:

if (evA != 0 && evA == A_EVENT_1) {
    B_do_something();
}
if (evB != 0 && evB == B_EVENT_C) {
    A_do_something();
}

So, even if I could create two threads and separate functionality from libraries, these threads would have to exchange events among them (probably through pipe).因此,即使我可以创建两个线程并将功能与库分开,这些线程也必须在它们之间交换事件(可能通过管道)。 This would still limit performance, because one thread would be blocked by *X_wait_for_event()* function, and would not be possible to receive data immediately from other thread.这仍然会限制性能,因为一个线程会被 *X_wait_for_event()* function 阻塞,并且不可能立即从其他线程接收数据。

How to solve this?如何解决这个问题?

This solution may not be available depending on the libraries you're using, but the best solution is not to call functions in individual libraries that wait for events.根据您使用的库,此解决方案可能不可用,但最好的解决方案是不要调用等待事件的各个库中的函数。 Each library should support hooking into an external event loop.每个库都应该支持连接到外部事件循环。 Then your application uses a single loop which contains a poll() or select() call that waits on all of the events that all of the libraries you use want to wait for.然后您的应用程序使用一个循环,其中包含一个poll()select()调用,该循环等待您使用的所有库想要等待的所有事件。

glib's event loop is good for this because many libraries already know how to hook into it. glib 的事件循环对此很有帮助,因为许多库已经知道如何挂钩它。 But if you don't use something as elaborate as glib, the normal approach is this:但是如果你不使用像 glib 这样复杂的东西,通常的方法是这样的:

  • Loop forever:永远循环:
    • Start with an infinite timer and an empty set of file descriptors从一个无限计时器和一组空的文件描述符开始
    • For each library you use:对于您使用的每个库:
      • Call a setup function in the library which is allowed to add file descriptors to your set and/or shorten (but not lengthen) the timeout.在库中调用设置 function 允许将文件描述符添加到您的集合和/或缩短(但不能延长)超时。
    • Run poll()运行poll()
    • For each library you use:对于您使用的每个库:
      • Call a dispatch function in the library that responds to any events that might have occurred when the poll() returned.在库中调用调度 function 以响应poll()返回时可能发生的任何事件。

Yes, it's still possible for an earlier library to starve a later library, but it works in practice.是的,早期的图书馆仍然有可能让后来的图书馆饿死,但它在实践中是有效的。

If the libraries you use don't support this kind of setup & dispatch interface, add it as a feature and contribute the code upstream!如果您使用的库不支持这种设置和调度接口,请将其添加为功能并向上游贡献代码!

(I'm moving this to an answer since it's getting too long for a comment) (我将其移至答案,因为评论时间太长了)

If you are in a situation where you're not allowed to call A_do_something in one thread while another thread is executing A_wait_for_event (and similarly for B ), then I'm pretty sure you can't do anything efficient, and have to settle between various evils.如果您处于不允许在一个线程中调用A_do_something而另一个线程正在执行A_wait_for_event (对于B也是如此)的情况,那么我很确定您无法做任何有效的事情,并且必须在两者之间解决各种邪恶。

The most obvious improvement is to immediately take action upon getting an event, rather than trying to read from both: ie order your loop最明显的改进是在收到事件后立即采取行动,而不是尝试从两者中读取:即订购您的循环

  • Wait for an A event等待 A 事件
  • Maybe do something in B也许在 B 做点什么
  • Wait for a B event等待 B 事件
  • Maybe do something in A也许在A做点什么

Other mitigations you could do are您可以做的其他缓解措施是

  • Try to predict whether an A event or a B event is more likely to come next, and wait on that first.尝试预测接下来发生的是 A 事件还是 B 事件,然后先等待。 (eg if they come in streaks, then after getting and handling an A event, you should go back to waiting for another A event) (例如,如果它们连续出现,那么在获取并处理 A 事件后,您应该 go 返回等待另一个 A 事件)
  • Fiddle with the timeout values to strike a balance between spin loops and too much blocking.摆弄超时值以在自旋循环和过多阻塞之间取得平衡。 (maybe even adjust dynamically) (甚至可以动态调整)

EDIT: You might check the APIs for your library;编辑:您可以检查您的图书馆的 API; they might already offer a way to deal with the problem.他们可能已经提供了解决问题的方法。 For example, they might allow you to register callbacks for events, and get notifications of events through the callback, rather than polling wait_for_event .例如,它们可能允许您为事件注册回调,并通过回调获取事件通知,而不是轮询wait_for_event

Another thing is if you can create new file descriptors for the library to listen on.另一件事是你是否可以为库创建新的文件描述符来监听。 eg If you create a new pipe and hand one end to library A , then if thread #1 is waiting for an A event, thread #2 can write to the pipe to make an event happen, thus forcing #1 out of wait_for_event .例如,如果您创建一个新的 pipe 并将一端交给库A ,那么如果线程 #1 正在等待A事件,线程 #2 可以写入 pipe 以使事件发生,从而迫使 #1 退出wait_for_event With the ability to kick threads out of the wait_for_event functions at will, all sorts of new options become available.由于能够随意将线程踢出wait_for_event函数,各种新选项都变得可用。

A possible solution is to use two threads to wait_for_events plus boost::condition_variable in "main" thread which "does something".一个可能的解决方案是在“做某事”的“主”线程中使用两个线程来wait_for_events加上boost::condition_variable An alike but not exact solution is here一个相似但不精确的解决方案在这里

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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