我正在编写一个小型服务器,它将从多个源接收数据并处理这些数据。这些源和接收到的数据都很重要,但 epoll 应该能够很好地处理。然而,所有接收到的数据都必须被解析并通过大量测试,这是耗时的,并且尽管使用 epoll 多路复用,仍会阻塞单个线程。基本上,模式应该像以下内容:IO 循环接收数据并将其打包成作业,发送给池中可用的第一个线程,该捆绑包由作业处理,结果传递回 IO 循环以写入文件。
我已决定使用一个 IO 线程和 N 个工作线程。使用提供的示例实现接受 tcp 连接和读取数据的 IO 线程非常容易,参见: http://linux.die.net/man/7/epoll
线程通常也很容易处理,但我正在努力以优雅的方式将 epoll IO 循环与线程池相结合。我无法在网上找到任何关于使用带有 worker 池的 epoll 的“最佳实践”,但有很多关于同一主题的问题。
因此,我有一些问题,希望有人能帮我回答:
- 可以(并且应该)使用 eventfd 作为 IO 线程和所有工作线程之间的双向同步机制吗?例如,每个 worker 线程是否应具有自己的 epoll 例程,等待共享事件 (带有包含数据/关于作业信息的结构体指针) ,即以某种方式使用 eventfd 作为作业队列?还可以从多个 worker 线程传递另一个 eventfd 将结果传回 IO 线程吗?
- 在套接字上通知 IO 线程有更多数据后,接收实际数据是应该在 IO 线程上进行,还是应该由工作者线程自行接收数据以避免阻塞 IO 线程,同时解析数据帧等?在这种情况下,如何确保安全性,例如如果 recv 在一个 worker 线程中读取了 1.5 帧数据,而另一个 worker 线程从同一连接接收了最后的 0.5 帧数据。
编辑:一种可能的解决方案是从IO循环更新环形缓冲区,在更新后将环形缓冲区索引通过共享管道发送给所有工作线程(因此将该索引的控制权交给第一个读取管道中索引的工作线程),让工作线程拥有该索引直到处理结束,然后再次通过管道将索引号发送回IO线程,从而归还控制权。
我的应用程序仅限于Linux,因此我可以使用Linux特有的功能以最优雅的方式实现。不需要跨平台支持,但需要考虑性能和线程安全性。