Java 网络编程的推荐做法。可惜伸缩性不佳。
2.
使用线程池,同样使用阻塞式 IO 操作。与 1 相比,这是提高性能的措施。
3. 使用 non-blocking IO + IO multiplexing 。即 Java NIO 的方式。
4. Leader/Follower 等高级模式
在默认情况下,我会使用第 3 种,即 non-blocking IO + one loop per thread 模式 。
http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod#THREADS_AND_COROUTINES
One loop per thread
此种模型下,程序里的每个 IO 线程有一个 event loop (或者叫 Reactor
) ,用于处
理
读写和定时事件(无论周期性的还是单次的) ,代码框架跟第 2 节一样。
这种方式的好处是:
�
线程数目基本固定,可以在程序启动的时候设置,不会频繁创建与销毁。
� 可以很方便地在线程间调配负载。
event loop 代表了线程的主循环,需要让哪个线程干活,就把 timer 或 IO channel
(TCP connection) 注册到那个线程的 loop 里即可。对实时性有要求的 connection 可
以单独用一个线程;数据量大的 connection 可以独占一个线程,并把数据处理任务 分
摊到另几个线程中;其他次要的辅助性 connections 可以共享一个线程。
对于 non-trivial 的服务端程序,一般会采用 non-blocking IO + IO multiplexing ,每个
connection/acceptor 都会注册到某个 Reactor 上,程序里有多个 Reactor ,每个线程至多
有一个 Reactor 。
多线程程序对 Reactor 提出了更高的要求,那就是 “ 线程安全
”
。要允许一个线程往 别
的线程的 loop 里塞东西,这个 loop 必须得是线程安全的。
线程池
不过,对于没有 IO 光有计算任务的线程,使用 event loop 有点浪费,我会用有一种
补充方案,即用 blocking queue 实现的任务队列 (TaskQueue) :
blocking_queue<boost::function<void()> > taskQueue; // 线程安全的阻塞队列
void worker_thread()
{
while (!quit) {
boost::function<void()> task = taskQueue.take(); // this blocks
task(); // 在产品代码中需要考虑异常处理
}
}
用这种方式实现线程池特别容易:
启动容量为 N 的线程池:
int N = num_of_computing_threads;
for (int i = 0; i < N; ++i) {
create_thread(&worker_thread); // 伪代码:启动线程
}