[英]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 这样复杂的东西,通常的方法是这样的:
poll()
poll()
poll()
returned.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最明显的改进是在收到事件后立即采取行动,而不是尝试从两者中读取:即订购您的循环
Other mitigations you could do are您可以做的其他缓解措施是
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
函数,各种新选项都变得可用。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.