//用blocking queue实现任务队列的线程池
typedef boost::function<void()> Functor;
BlockingQueue<Functor> taskQueue; //线程安全的阻塞队列
void workerThread()
{
while(running) //running变量是全局标志
{
Functor task = taskQueue.take(); //this blocks
task(); //在产品代码中需要考虑异常处理
}
}
//启动容量(并发数)为N的线程池:
int N = num_of_computing_threads;
for(int i = 0;i < N;++i)
{
create_thread(&workerThread); //伪代码:启动线程
}
//使用
Foo foo; //Foo有calc()成员函数
boost::function<void()> task = boost::bind(&Foo::calc,&foo);
taskQueue.post(task);
c++多线程服务器编程模式为:one (event)loop per thread + thread pool
TCP能跨语言,服务端和客户端可以不同语言。
分布式系统的软件设计和功能划分一般是以进程为单位。进程间采用TCP长连接通信。
长连接的好处:
1、容易定位分布式系统中的服务之间的依赖关系。(利用netstat和lsof查看)
2、通过接收和发送队列的长度也比较容易定位网络或程序故障。
开发服务端程序的一个任务就是处理并发连接。
1、当线程廉价时,一台机器上可以创建超过CPU数目的线程,这时一个线程只处理一个TCP连接。通常使用阻塞IO。例如:Python gevent 、Go goroutine、Erlang actor。
2、当线程宝贵时,一台机器上只能创建于CPU数目相当的线程。这时一个线程要处理多个TCP连接上的IO,通常使用非阻塞IO和IO multiplexing。例如:libevent、muduo、Netty。
可用模式:
1、运行一个单线程的进程。
2、运行一个多线程的进程。
3、运行多个单线程的进程。
4、运行多个多线程的进程。
模式3有两个子模式:1、简单的把模式1的进程运行多份。2、主进程+worker进程,如果绑定到TCP port,比如httpd+fastcgi。
必须使用单线程的场合:
1、程序可能会fork(2);
2、限制程序的CPU占用率。
一个程序fork(2)之后的一般行为:
1、立刻执行exec(),变身为另一个程序。
2、不调用exec(),继续运行当前程序。要么通过共享的文件描述符和父进程通信,协同完成任务;要么接过父进程传来的文件描述符,独立完成工作。
看门狗进程必须是单线程。
适用于多线程的场景:提高响应速度,让IO和“计算”相互重叠,降低latency。虽然不能提高绝对性能,但能提高平均响应性能。
Latency,中文译作延迟。Throughput,中文译作吞吐量。它们是衡量软件系统的最常见的两个指标。
多线程满足:
1、多个CPU可用。
2、线程间有共享数据,即内存中的全局状态。
3、共享数据可修改,而不是静态的常量表。
4、提供非均质的服务。
5、latency和throughput同样重要,不是逻辑简单的IO bound或CPU bound程序。换言之,程序要有相当的计算量。
6、利用异步操作。
7、能scale up。
8、具有可预测的性能。
9、有效划分责任与功能,让每个线程的逻辑比较简单,任务单一,便于编码。
线程分类:
1、IO线程。这类线程的主要循环是IO multiplexing,阻塞地等在select/poll/epoll_wait系统调用上。
2、计算线程。这类线程的主要循环是blocking queue,阻塞地等在condition variable上。这类线程一般位于Thread pool中,这种线程通常不涉及IO,一般避免任何阻塞操作。
3、第三方库所用的线程。
linux中一个线程默认栈10M