关于linux:linux下多线程与并发服务器设计方案及常见问题

5次阅读

共计 2584 个字符,预计需要花费 7 分钟才能阅读完成。

一、基础知识

1、一个主机的端口号为所有过程所共享,但普通用户过程绑定 bind 不了一些非凡端口号如 20、80 等。

多个过程不能同时监听 listen 同一个端口,会失败。当然父过程能够先 listen 而后 fork 多个子过程,多个子过程都能够 accept 这个 sock,即争夺式响应(惊群效应)。

关注 4 元组是否能惟一确定一个连贯?

2、每个过程都有本人的文件描述符(包含 file fd, socket fd, timer fd, event fd, signal fd),个别是 1024,能够通过 ulimit -n 设置,但所有过程关上的文件描述符总数有下限,跟主机的内存无关。

3、一个过程内的所有线程共享过程的文件描述符。

二、并发服务器计划:

1、循环式 / 迭代式 (iterative) 服务器

无奈充分利用多核 CPU,不适宜执行工夫较长的服务,即实用于短连贯。如果是长连贯则须要在 read/write 之间循环,那么只能服务一个客户端。

2、并发式 (concurrent) 服务器

one connection per process/one connection per thread

适宜执行工夫比拟长的服务

one connection per process : 主过程每次 fork 之后要敞开 connfd,子过程要敞开 listenfd

one connection per thread : 主线程每次 accept 回来就创立一个子线程服务,因为线程共享文件描述符,故不必敞开。

3、prefork or pre threaded(容易产生“惊群”景象,即多个子过程都处于 accept 状态)

4、反应式 (reactive) 服务器 (reactor 模式)(select/poll/epoll)

并发解决多个申请,实际上是在一个线程中实现。无奈充分利用多核 CPU

不适宜执行工夫比拟长的服务,所以为了让客户感觉是在“并发”解决而不是“循环”解决,每个申请必须在绝对较短时间内执行。

5、reactor + thread per request(过渡计划)

6、reactor + worker thread(过渡计划)

7、reactor + thread pool(能适应密集计算)

muduo 库中的 /example/suduku/ 中有这样一个例子,因为数独求解是计算密集型工作。

在实践中为了 reactor 能疾速回到事件循环去响应申请,常常将读到的数据 put 到一个环形内存队列(个别内存 or 共享内存),而 thread pool 的线程则从中读取进行数据计算。

须要 C /C++ Linux 服务器架构师学习材料加群 812855908(材料包含 C /C++,Linux,golang 技术,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK,ffmpeg 等),收费分享

8、multiple reactors(能适应更大的突发 I /O)

reactors in threads(one loop per thread)

reactors in processes

一般来说一个 subReactor 实用于一个千兆网口

9、multiple reactors + thread pool(one loop per thread + threadpool)(突发 I / O 与密集计算)

subReactor 能够有多个,但 threadpool 只有一个。

10、proactor 服务器(proactor 模式,基于异步 I /O)

实践上 proactor 比 reactor 效率要高一些

异步 I / O 可能让 I / O 操作与计算重叠。充分利用 DMA 个性。

Linux 异步 IO

glibc aio(aio_*),有 bug

kernel native aio(io_*),也不完满。目前仅反对 O_DIRECT 形式来对磁盘读写,跳过零碎缓存。要自已实现缓存,难度不小。

boost asio 实现的 proactor,实际上不是真正意义上的异步 I /O,底层是用 epoll 来实现的,模仿异步 I / O 的。

常见并发服务器计划比拟:

三、一些常见问题

1、Linux 能同时启动多少个线程?

对于 32-bit Linux,一个过程的地址空间是 4G,其中用户态能拜访 3G 左右,而一个线程的默认栈 (stack) 大小是 8M,心算可知,一个过程大概最多能同时启动 350 个线程左右。

2、多线程能进步并发度吗?

如果指的是“并发连接数”,不能。

如果单纯采纳 thread per connection 的模型,那么并发连接数大概 350,这远远低于基于事件的单线程程序所能轻松达到的并发连接数(几千上万,甚至几万)。所谓“基于事件”,指的是用 IO multiplexing event loop 的编程模型,又称 Reactor 模式。

3、多线程能进步吞吐量吗?

对于计算密集型服务,不能。

如果要在一个 8 核的机器上压缩 100 个 1G 的文本文件,每个 core 的解决能力为 200MB/s,那么“每次起 8 个过程,一个过程压缩一个文件”与“只启动一个过程(8 个线程并发压缩一个文件)”,这两种形式总耗时相当,然而第二种形式能较快的拿到第一个压缩完的文件。

4、多线程能进步响应工夫吗?

能够。参考问题 3

5、多线程程序日志库要求

线程平安,即多个线程能够并发写日志,两个线程的日志音讯不会呈现交错。

用一个全局的 mutex 爱护 IO

每个线程独自写一个日志文件

前者造成全副线程抢占一个锁(串行写入)

后者有可能让业务线程阻塞在写磁盘操作上。(磁盘 IO 工夫比拟长)

解决办法:用一个 logging 线程负责收集日志音讯,并写入日志文件,其余业务线程只管往这个“日志线程”发送日志音讯(如通过 BlockingQueue 提供接口),这称为“异步日志”,也是一个经典的生产者消费者模型。

6、线程池大小的抉择

如果池中执行工作时,密集计算所占工夫比重为 P(0<P<=1),而零碎一共有 C 个 CPU,为了让 C 个 CPU 跑满而不过载,线程池大小的教训公式 T =C/P,即 T *P=C(让 CPU 刚好跑满)

假如 C =8,P=1.0,线程池的工作齐全密集计算,只有 8 个流动线程就能让 CPU 饱和

假如 C =8,P=0.5,线程池的工作有一半是计算,一半是 IO,那么 T =16,也就是 16 个“50% 忙碌的线程”能让 8 个 CPU 忙个不停。

7、线程分类

I/ O 线程(这里特指网络 I /O)

计算线程

第三方库所用线程,如 logging, 又比方 database

正文完
 0