最近组内争论,结果发现不用多线程了

bihai
楼主 (未名空间)

C++项目中看到一个两年前的代码很类似,就抄了一下。结果,审查的时候追着问为什
么多线程?

主要我不知道单线程怎么做。结果方向是异步。本来项目就是一个消息循环处理各类的,然后各种callback。关键我们这个需求本来有一个client,但是还必须提供一个
server,多线程一共三行代码来启动这个server,另一个线程专注client端。

如果改成单线程异步需要改动不小,主要是我们其中每步都要完成后再继续,其中之一就是需要server收到一个消息之后再继续。用多线程等在那里非常简单。改成异步,我估计可以。但是代码量超过十几行。只是同一个线程既处理客户端又处理服务器有些不可思议。
l
lestrois2000

要看多线程里的IO block程度而定。
bihai

具体怎么讲?
【 在 lestrois2000 (lestrois2000) 的大作中提到: 】
: 要看多线程里的IO block程度而定。

l
lestrois2000

创建线程的缺陷在于如果线程过多且每个线程内IO Block时间过长,那么大量的线程是在等待IO的状态,使得CPU利用率实际上不高,创建多线程意义不大,开销还不小,是
经典的C10K problem。解决的方法是用单线程,或少量线程,利用OS的EPoll/Kqueue来进行event loop,提高效率,降低资源利用率。
【 在 bihai (学得不好) 的大作中提到: 】
: 具体怎么讲?

bihai

你说的可以使用线程池解决吧。

我们这个就这个类有这个一次性阻塞,大概需要十几秒写磁盘的任务。然后即继续干下一步。其他任务和这个没关系。其他任务没有多线程。

【 在 lestrois2000 (lestrois2000) 的大作中提到: 】
: 创建线程的缺陷在于如果线程过多且每个线程内IO Block时间过长,那么大量的线程是
: 在等待IO的状态,使得CPU利用率实际上不高,创建多线程意义不大,开销还不小,是
: 经典的C10K problem。解决的方法是用单线程,或少量线程,利用OS的EPoll/Kqueue来
: 进行event loop,提高效率,降低资源利用率。

netghost

你這個情況用多線程沒有什麼問題。很多人估計從來不知道操作系統怎麼處理io的,然後就學了一堆的什麼異步之類的buzzword,純粹瞎搞。

【 在 bihai (学得不好) 的大作中提到: 】
: 你说的可以使用线程池解决吧。
: 我们这个就这个类有这个一次性阻塞,大概需要十几秒写磁盘的任务。然后即继续干下
: 一步。其他任务和这个没关系。其他任务没有多线程。

BroPingtou

参考下redis的设计?
https://zhuanlan.zhihu.com/p/52600663
p
pptwo

从javascript开始到最近几乎所有语言一窝蜂上coroutine,单线程消息循环callback
图的就是一个不用加锁写起来不费脑子,小白程序员也不会犯啥错。你把它改成多线
程的,锁怎么加,加到哪个粒度都不是个简单的事情,人家不愿意费这个工夫,只能
让你改回去。

【 在 bihai (学得不好) 的大作中提到: 】
: C++项目中看到一个两年前的代码很类似,就抄了一下。结果,审查的时候追着问为什
: 么多线程?
: 主要我不知道单线程怎么做。结果方向是异步。本来项目就是一个消息循环处理各类的
: ,然后各种callback。关键我们这个需求本来有一个client,但是还必须提供一个
: server,多线程一共三行代码来启动这个server,另一个线程专注client端。
: 如果改成单线程异步需要改动不小,主要是我们其中每步都要完成后再继续,其中之一
: 就是需要server收到一个消息之后再继续。用多线程等在那里非常简单。改成异步,我
: 估计可以。但是代码量超过十几行。只是同一个线程既处理客户端又处理服务器有些不
: 可思议。

netghost

你想多了,就是buzzword這幾年火了。而且消息循環不是win32時代的標準做法麼?

實際上,一定要比這種亂callback的程序更沒法debug。
【 在 pptwo (pp) 的大作中提到: 】
: 标 题: Re: 最近组内争论,结果发现不用多线程了
: 发信站: BBS 未名空间站 (Tue Sep 21 10:52:31 2021, 美东)
:
: 从javascript开始到最近几乎所有语言一窝蜂上coroutine,单线程消息循环
callback
: 图的就是一个不用加锁写起来不费脑子,小白程序员也不会犯啥错。你把它改成多线: 程的,锁怎么加,加到哪个粒度都不是个简单的事情,人家不愿意费这个工夫,只能: 让你改回去。
:
: 【 在 bihai (学得不好) 的大作中提到: 】
: : C++项目中看到一个两年前的代码很类似,就抄了一下。结果,审查的时候追着问
为什
: : 么多线程?
: : 主要我不知道单线程怎么做。结果方向是异步。本来项目就是一个消息循环处理各类的
: : ,然后各种callback。关键我们这个需求本来有一个client,但是还必须提供一个: : server,多线程一共三行代码来启动这个server,另一个线程专注client端。
: : 如果改成单线程异步需要改动不小,主要是我们其中每步都要完成后再继续,其中之一
: : 就是需要server收到一个消息之后再继续。用多线程等在那里非常简单。改成异步,我
: : 估计可以。但是代码量超过十几行。只是同一个线程既处理客户端又处理服务器有些不
: : 可思议。
:
:
:
: --
:
bihai

没有锁。主线程生成一个数据结构,给新线程使用,但是主线程自己不用。主线程函数调用后就等在那里。函数调用结束了,主线程就继续。这个时候新线程也结束了。

【 在 pptwo (pp) 的大作中提到: 】
: 从javascript开始到最近几乎所有语言一窝蜂上coroutine,单线程消息循环
callback
: 图的就是一个不用加锁写起来不费脑子,小白程序员也不会犯啥错。你把它改成多线: 程的,锁怎么加,加到哪个粒度都不是个简单的事情,人家不愿意费这个工夫,只能: 让你改回去。

netghost

你這樣的話,爲啥要起一個新線程,直接一個線程走完不就完事了。
【 在 bihai (学得不好) 的大作中提到: 】
: 没有锁。主线程生成一个数据结构,给新线程使用,但是主线程自己不用。主线程函数
: 调用后就等在那里。函数调用结束了,主线程就继续。这个时候新线程也结束了。
: callback

lightroom

这不是最简单producer consumer的吗?

【在 bihai(学得不好)的大作中提到:】
:没有锁。主线程生成一个数据结构,给新线程使用,但是主线程自己不用。主线程函数调用后就等在那里。函数调用结束了,主线程就继续。这个时候新线程也结束了。


g
guvest

“ 函数调用结束了,”

这函数什么条件下return?

【 在 bihai(学得不好) 的大作中提到: 】

: 没有锁。主线程生成一个数据结构,给新线程使用,但是主线程自己不用。主线程函数

: 调用后就等在那里。函数调用结束了,主线程就继续。这个时候新线程也结束了。

: callback

bihai

客户端调用的时候,提供了一个类的实例作为参数,然后阻塞在那里;
被调用的程序通过这个实例进行一些数据的读取;但是因为原线程的阻塞,无法进行下去。这里,应该认为不是被调用的程序直接调用客户端,而是间接,比如通过网络。客户端是否响应是客户端自己决定的。

客户端调用这个函数的时候也不是直接调用,而是间接的通过系统。客户端自己看来就阻塞了。系统负责通知被调用的程序获得所有的信息,和进行工作。

【 在 guvest (我爱你老婆Anna) 的大作中提到: 】
: “ 函数调用结束了,”
: 这函数什么条件下return?
:
: 没有锁。主线程生成一个数据结构,给新线程使用,但是主线程自己不用。主线
: 程函数
:
: 调用后就等在那里。函数调用结束了,主线程就继续。这个时候新线程也结束了。
:
: callback
:

bihai

我今天才学到一个线程怎么干。总的来说,我们这个系统就没看到几个线程的例子。说是有一些系统的操作导致线程不安全。我们随便写一个类,就会在一个线程启动,如果我们开了新线程,就可能在新线程销毁,这是就会出错。

【 在 netghost (Up to Isomorphism) 的大作中提到: 】
: 你這樣的話,爲啥要起一個新線程,直接一個線程走完不就完事了。

digua

你这个帖子我有些疑问。改成异步,代码量超过十几行,你说的真的是十几行吗?如果是这样,为什么纠结呢?

我记得你以前说过是搞嵌入式系统的?异步编程是嵌入式编程的基本功。在嵌入式应用中,异步一般比多线程效率更高。处理器性能足够时,表面上看不出来差别,但在能效上总是有差别的。

【 在 bihai (学得不好) 的大作中提到: 】
: C++项目中看到一个两年前的代码很类似,就抄了一下。结果,审查的时候追着问为什
: 么多线程?
: 主要我不知道单线程怎么做。结果方向是异步。本来项目就是一个消息循环处理各类的
: ,然后各种callback。关键我们这个需求本来有一个client,但是还必须提供一个
: server,多线程一共三行代码来启动这个server,另一个线程专注client端。
: 如果改成单线程异步需要改动不小,主要是我们其中每步都要完成后再继续,其中之一
: 就是需要server收到一个消息之后再继续。用多线程等在那里非常简单。改成异步,我
: 估计可以。但是代码量超过十几行。只是同一个线程既处理客户端又处理服务器有些不
: 可思议。

netghost

OK,那就是你們的tech lead不讓你能在一個代碼模塊裏面用兩個CPU,調度要他管。這屬於政治問題,和技術關係不大。
【 在 bihai (学得不好) 的大作中提到: 】
: 我今天才学到一个线程怎么干。总的来说,我们这个系统就没看到几个线程的例子。说
: 是有一些系统的操作导致线程不安全。我们随便写一个类,就会在一个线程启动,如果
: 我们开了新线程,就可能在新线程销毁,这是就会出错。

bihai

现在懂了,就不难了。

我们的架构是用promise来等待任务完成。任务本身就是一个函数,比如写磁盘,同步
阻塞很容易懂。但是如果是异步,要等callback之后才知道结束了,这个函数怎么写来和promise匹配就不清楚了。后来给了一个办法很麻烦。昨天有个人告诉我可以直接处
理消息循环。结果代码行数和现在一样,而且基本是不变的,只是把额外的线程去掉了。这个我就容易理解了。

【 在 digua (姚之FAN) 的大作中提到: 】
: 你这个帖子我有些疑问。改成异步,代码量超过十几行,你说的真的是十几行吗?如果
: 是这样,为什么纠结呢?
: 我记得你以前说过是搞嵌入式系统的?异步编程是嵌入式编程的基本功。在嵌入式应用
: 中,异步一般比多线程效率更高。处理器性能足够时,表面上看不出来差别,但在能效
: 上总是有差别的。

bihai

目前还没看到使用多CPU多线程干事情的。不行就多进程吧。

【 在 netghost (Up to Isomorphism) 的大作中提到: 】
: OK,那就是你們的tech lead不讓你能在一個代碼模塊裏面用兩個CPU,調度要他管。這
: 屬於政治問題,和技術關係不大。

h
haidafu

async + event_base + thread pool vs 多线程
请教一下: async 到底优势在哪?下面理解对不对。
1)thread pool的线程数通常小很多,又不是dynamic create.这样减少了thread
create and destory的开销。这个是async + pool主要的优势。
2)thread pool可以采用last-finished-first-scheduled方式,这样可以减少context switch的cost.
3) thread如果blocking, 并不浪费cpu resource 因为 Linux kernel会把 cpu core
用给其它的thread. 也不会占用很多内存, madvise会利用thread stack

【 在 lestrois2000 (lestrois2000) 的大作中提到: 】
: 创建线程的缺陷在于如果线程过多且每个线程内IO Block时间过长,那么大量的线程是
: 在等待IO的状态,使得CPU利用率实际上不高,创建多线程意义不大,开销还不小,是
: 经典的C10K problem。解决的方法是用单线程,或少量线程,利用OS的EPoll/Kqueue来
: 进行event loop,提高效率,降低资源利用率。

l
lestrois2000

准确地比较是async + event-loop vs 多线程(thread pool),可能你被lz回复我的
帖子时候的所谓的解决方法误导了,其实在这个讨论中线程池不起到决定性作用,即使是用到了类似Java的ForkJoinPool的work stealing。在很多情况下work stealing派不上用场,比如一台服务器同时连接10K的TCP connection,都是长连接而不是此起彼伏
的连接与断开,那么相同的thread就很少几率给其他task使用,也就无从谈起stealing。这回答了你的问题2)。

对于问题1,在我的回答里我提到了两个前提,一个是IO block时间长,一个是线程过
多,很多情况下是不能限制线程池数量的,因为请求可能非常大,限制线程数量不能解决大量请求。

对于问题3,因为IO阻塞时间过长CPU需求小,即使CPU被分给其他线程,其他线程的CPU需求量也不大,因为其他线程的IO阻塞时间也长CPU需求也小。

讨论的重点是看的是IO阻塞的时间比CPU时间过大,CPU并没有在多线程的情况下提高利用率,那么就给了我们一个想法,与其这样,不如回归单线程,还能节省开销。这也是当初Linux引入Epoll的初衷,他来自业界的呼唤。当时的Tomcat的连接就需要一个连接建立一个线程,当用户数量激增时,服务器线程过多而崩溃,分析后得出服务线程的大部分时间是读数据库/读磁盘/读写socket buffer,而这些都不需要cpu直接参与,尤其是在做代理或load balancer的时候,没有任何的CPU数据处理的需要。经过epoll引入
linux后,Nginx利用其开发的单线程服务器效率更高,开销更小,轻松处理10K数量的
连接。还有前面有人贴的Redis里的单线程的例子,道理相同。

EPoll的原理可以看看网上的介绍,看看这个youtube也不错 https://www.youtube.com/watch?v=4gv9reEaD7g&ab_channel=TimPEI

【 在 haidafu (海大富) 的大作中提到: 】
: async + event_base + thread pool vs 多线程
: 请教一下: async 到底优势在哪?下面理解对不对。
: 1)thread pool的线程数通常小很多,又不是dynamic create.这样减少了thread
: create and destory的开销。这个是async + pool主要的优势。
: 2)thread pool可以采用last-finished-first-scheduled方式,这样可以减少
context
: switch的cost.
: 3) thread如果blocking, 并不浪费cpu resource 因为 Linux kernel会把 cpu
core
: 用给其它的thread. 也不会占用很多内存, madvise会利用thread stack


netghost

這兩個東西其實並沒有必然聯繫,覺得可以相比是因爲現在太多編程語言喜歡hide I/O和操作系統的細節的結果。

【 在 haidafu (海大富) 的大作中提到: 】
: async + event_base + thread pool vs 多线程
: 请教一下: async 到底优势在哪?下面理解对不对。
: 1)thread pool的线程数通常小很多,又不是dynamic create.这样减少了thread
: create and destory的开销。这个是async + pool主要的优势。
: 2)thread pool可以采用last-finished-first-scheduled方式,这样可以减少
context
: switch的cost.
: 3) thread如果blocking, 并不浪费cpu resource 因为 Linux kernel会把 cpu
core
: 用给其它的thread. 也不会占用很多内存, madvise会利用thread stack

l
lestrois2000

至于lz的例子,可能还真不需要改成单线程,因为我觉得他的线程的数量也不会大到哪里去。不过既然大部分人都这样说,也是对另一种线程模式的学习,好像也是一个趋势,改改也无妨。
heteroclinic

这个还是要有具体的业务分析和建模
楼主记得是电网系统的,基本是要求mission critical 和高可靠性
这样就要求设计尽可能简化,要坚决杜绝各种framework
开发要基于自己硬件支持的C
互联网公司的来给人支招,我觉得不太合适,这个就是想到哪里说哪里。
就拿我举的shell 里的ls 程序。就是一个思路,要写一个程序主要要考虑的问题。
我一个问题: 就是你看了ls的源码你最大的感触是什么?如果你的答案基本和我的一
致,那么我觉得多线程与否,不是问题,说明你有这个能力。
这个大公司里各把一块,说是官僚一些,这也是系统稳定性的冗余。
bihai

我们倒是确实是要求高可靠性,虽然这个项目运行时间很短,只运行几次,但是非常重要,跟发射火箭似的。上一次200个设备有一个出了多线程问题。这次吸取了教训。不过据说一些库以后会改变,这样就可以线程安全了。这之前估计就不写多线程了。

【 在 heteroclinic (asymptotically stable) 的大作中提到: 】
: 这个还是要有具体的业务分析和建模
: 楼主记得是电网系统的,基本是要求mission critical 和高可靠性
: 这样就要求设计尽可能简化,要坚决杜绝各种framework
: 开发要基于自己硬件支持的C
: 互联网公司的来给人支招,我觉得不太合适,这个就是想到哪里说哪里。
: 就拿我举的shell 里的ls 程序。就是一个思路,要写一个程序主要要考虑的问题。
: 我一个问题: 就是你看了ls的源码你最大的感触是什么?如果你的答案基本和我的一
: 致,那么我觉得多线程与否,不是问题,说明你有这个能力。
: 这个大公司里各把一块,说是官僚一些,这也是系统稳定性的冗余。

iDemocracy

恕我冒昧,总感觉这是把软件设计的漏洞用水泥给堵上了。本来应该用锁解决的问题。

关于并发模型跟物理模型的关系,以后聊,不过这个例子好像跟物理规律不齐心。

【 在 bihai (学得不好) 的大作中提到: 】
: C++项目中看到一个两年前的代码很类似,就抄了一下。结果,审查的时候追着问为什
: 么多线程?
: 主要我不知道单线程怎么做。结果方向是异步。本来项目就是一个消息循环处理各类的
: ,然后各种callback。关键我们这个需求本来有一个client,但是还必须提供一个
: server,多线程一共三行代码来启动这个server,另一个线程专注client端。
: 如果改成单线程异步需要改动不小,主要是我们其中每步都要完成后再继续,其中之一
: 就是需要server收到一个消息之后再继续。用多线程等在那里非常简单。改成异步,我
: 估计可以。但是代码量超过十几行。只是同一个线程既处理客户端又处理服务器有些不
: 可思议。
h
helpme

看不懂你的中文解释,不能贴点pseudo code吗?

【 在 bihai (学得不好) 的大作中提到: 】
: C++项目中看到一个两年前的代码很类似,就抄了一下。结果,审查的时候追着问为什
: 么多线程?
: 主要我不知道单线程怎么做。结果方向是异步。本来项目就是一个消息循环处理各类的
: ,然后各种callback。关键我们这个需求本来有一个client,但是还必须提供一个
: server,多线程一共三行代码来启动这个server,另一个线程专注client端。
: 如果改成单线程异步需要改动不小,主要是我们其中每步都要完成后再继续,其中之一
: 就是需要server收到一个消息之后再继续。用多线程等在那里非常简单。改成异步,我
: 估计可以。但是代码量超过十几行。只是同一个线程既处理客户端又处理服务器有些不
: 可思议。

g
guvest

这个是正解。花一晚上写个程序测一下。
自然语言必定不如量化统计。千变万化,不如自己测试下,然后出个表格。

【 在 heteroclinic(asymptotically stable) 的大作中提到: 】

: 这个还是要有具体的业务分析和建模

: 楼主记得是电网系统的,基本是要求mission critical 和高可靠性

: 这样就要求设计尽可能简化,要坚决杜绝各种framework

: 开发要基于自己硬件支持的C

: 互联网公司的来给人支招,我觉得不太合适,这个就是想到哪里说哪里。

: 就拿我举的shell 里的ls 程序。就是一个思路,要写一个程序主要要考虑的问
题。

: 我一个问题: 就是你看了ls的源码你最大的感触是什么?如果你的答案基本和
我的一

: 致,那么我觉得多线程与否,不是问题,说明你有这个能力。

: 这个大公司里各把一块,说是官僚一些,这也是系统稳定性的冗余。

minquan

新版的C++11支持协程的写法。

你就把线程统统换成协程,应付了事。千万别自己手动写异步。
bihai

main:

Run(){
...
execute(阻塞式或者说是同步过程);
A a;
execute(a->work()); //此处也是阻塞式或同步过程
...
}

class A {
void work() {
C c;
Process b; // start a process
IPC(b, &c); // tell B to talk to c
WaitForAnswer(); // 同步等待
}
};

假定C在同一线程构造, 那么当WaitForAnswer阻塞式等待时,就无法响应B。如果
WaitForAnswer不阻塞,那么程序继续进行,execute()结束,Run就提前结束了,B还没开始问问题呢。

如果C是另一个线程,那么在A等待的时候,C可以响应。我看到别人两年前的例子就是
这样写的。他后面用的据说是安全的。

【 在 helpme (名虚胖字满肥) 的大作中提到: 】
: 看不懂你的中文解释,不能贴点pseudo code吗?

h
helpme

"
C c;
Process b; // start a process
IPC(b, &c); // tell B to talk to c
"

你都已经起了一个新的process(不明白为啥不用thread)了,为啥还要再给c开个新的thread?都在b那个process里synchronously做了不行吗?

【 在 bihai (学得不好) 的大作中提到: 】
: main:
: Run(){
: ...
: execute(阻塞式或者说是同步过程);
: A a;
: execute(a->work()); //此处也是阻塞式或同步过程
: ...
: }
: class A {
: void work() {
: ...................

netghost

你那個b起了進程是因爲那個是一個可執行文件你沒有source?我之前就說了,你一個
線程(或者就是進程)一竿到底沒什麼問題。

用shell script來說就是最簡單的管道: init_c; echo "talk to c" | b,這裏你分出去一個C線程沒意義,如果b不回你,b也不會talk to C。當然either way操作上都沒什麼問題。

說實話,爲啥這個並行搞了這麼多年,這麼簡單一件事還幹不過shell script,做語言和開發工具的人真的應該反思一下。

【 在 bihai (学得不好) 的大作中提到: 】
: main:
: Run(){
: ...
: execute(阻塞式或者说是同步过程);
: A a;
: execute(a->work()); //此处也是阻塞式或同步过程
: ...
: }
: class A {
: void work() {
: ...................

l
lestrois2000

感觉要首先解决的是第一个execute的阻塞问题,不能改成async的吗。
【 在 bihai (学得不好) 的大作中提到: 】
: main:
: Run(){
: ...
: execute(阻塞式或者说是同步过程);
: A a;
: execute(a->work()); //此处也是阻塞式或同步过程
: ...
: }
: class A {
: void work() {
: ...................

bihai

b是新的进程,关键是b又跟c问问题,可是c在a这个进程,是单线程,正阻塞呢

【 在 netghost (Up to Isomorphism) 的大作中提到: 】
: 你那個b起了進程是因爲那個是一個可執行文件你沒有source?我之前就說了,你一個
: 線程(或者就是進程)一竿到底沒什麼問題。
: 用shell script來說就是最簡單的管道: init_c; echo "talk to c" | b,這裏你分出
: 去一個C線程沒意義,如果b不回你,b也不會talk to C。當然either way操作上都沒什
: 麼問題。
: 說實話,爲啥這個並行搞了這麼多年,這麼簡單一件事還幹不過shell script,做語言
: 和開發工具的人真的應該反思一下。

h
helpme

你能把c挪到b的process/thread里面吗?连IPC都省了

【 在 bihai (学得不好) 的大作中提到: 】
: b是新的进程,关键是b又跟c问问题,可是c在a这个进程,是单线程,正阻塞呢

bihai

b早就写好了,就是那样的。所以我启动一个线程很容易。因为a即是客户端(b是服务
器),又是服务器(b是客户端)。

【 在 helpme (名虚胖字满肥) 的大作中提到: 】
: 你能把c挪到b的process/thread里面吗?连IPC都省了

h
haidafu

多谢回复。

>> 当时的Tomcat的连接就需要一个连接建立一个线程,当用户数量激增时,服务器线
程过多而崩溃,
请问,服务器线程过多而崩溃的原因是什么? out of cpu? out of memory? 到底线程
过多怎么造成崩溃?

async比sync好的本质原理是什么?如果都用thread pool的话,在创建thread的开销是一样的。

我看了这个(http://www.wangafu.net/~nickm/libevent-book/01_intro.html),可还是没想明白。多谢指教。

【 在 lestrois2000 (lestrois2000) 的大作中提到: 】
: 准确地比较是async + event-loop vs 多线程(thread pool),可能你被lz回复我的
: 帖子时候的所谓的解决方法误导了,其实在这个讨论中线程池不起到决定性作用,即使
: 是用到了类似Java的ForkJoinPool的work stealing。在很多情况下work stealing派不
: 上用场,比如一台服务器同时连接10K的TCP connection,都是长连接而不是此起彼伏
: 的连接与断开,那么相同的thread就很少几率给其他task使用,也就无从谈起
stealing
: 。这回答了你的问题2)。
: 对于问题1,在我的回答里我提到了两个前提,一个是IO block时间长,一个是线程过
: 多,很多情况下是不能限制线程池数量的,因为请求可能非常大,限制线程数量不能解
: 决大量请求。
: 对于问题3,因为IO阻塞时间过长CPU需求小,即使CPU被分给其他线程,其他线程的
CPU
: ...................

l
lestrois2000

共同探讨。你的链接的帖子就挺好的,基本上解释了为什么需要async,以及async方案从用户自己负责轮询到操作系统帮助轮询的演化。帖子的目的是推销它的Libevent lib,但是在之前他对这个问题的来龙去脉讲得很清楚。对于你的问题的重点看这段话:
```
(之前给出了多线程解决阻塞问题的代码)
So, do we have the perfect solution for handling multiple connections at
once? Can I stop writing this book and go work on something else now? Not
quite. First off, process creation (and even thread creation) can be pretty expensive on some platforms. In real life, you’d want to use a thread pool instead of creating new processes. But more fundamentally, threads won’t
scale as much as you’d like. If your program needs to handle thousands or
tens of thousands of connections at a time, dealing with tens of thousands
of threads will not be as efficient as trying to have only a few threads per CPU.
```
一台机器的cpu core数量有限,创建远超过其数量的的threads,即使用pool,也并没
有起到真正的并发运行的效果,而且增加不少开销。
这里又回到了那两个假设,一个是I/O阻塞是task的主要瓶颈,一个是并发数量很高(
原文里也提到handle thousands or tens of thousands of connections)。比如对于一台40 core的机器,并发量在100以下,多线程和async的方式在性能上和资源消耗上
不会有太大区别,如果并发量特小(很少的县城在等待),多线程的方案还会处理速度更快些。然而在并发量超过5倍的core的数量,事情就起了变化(太多的线程在等待),
选择async就是必然。“40core 100并发,5倍量”,这些数量只是个人经验,具体可以自己测试鉴别。鉴于OS已经提供了方便的async接口,如果我个人选择会一开始就做
async,即使是小并发量性能上可能会打些折扣。

【 在 haidafu (海大富) 的大作中提到: 】
: 多谢回复。
: >> 当时的Tomcat的连接就需要一个连接建立一个线程,当用户数量激增时,服务器线
: 程过多而崩溃,
: 请问,服务器线程过多而崩溃的原因是什么? out of cpu? out of memory? 到底线程
: 过多怎么造成崩溃?
: async比sync好的本质原理是什么?如果都用thread pool的话,在创建thread的开销是
: 一样的。
: 我看了这个(http://www.wangafu.net/~nickm/libevent-book/01_intro.html),可还是没想明白。多谢指教。
: stealing
: CPU

netghost

你不是本質就是要起一個程序要它根據你的要求給你發點結果,然後繼續麼?這些全是序列化的操作,沒什麼好並行的,你需要的只是等結果,沒了。
【 在 bihai (学得不好) 的大作中提到: 】
: b是新的进程,关键是b又跟c问问题,可是c在a这个进程,是单线程,正阻塞呢

c
chebyshev
https://www.chiark.greenend.org.uk/~sgtatham/coroutines.html

就这个pattern吧?
我很久前在本版聊过。
用GOTO,不用高端工具(包括cpp)可能有助于把逻辑想清楚。

【 在 bihai (学得不好) 的大作中提到: 】
: b早就写好了,就是那样的。所以我启动一个线程很容易。因为a即是客户端(b是服务
: 器),又是服务器(b是客户端)。

f
fantasist

代码全用coroutine的好处是,比如main::Run()里第一个execute和第二个execute也许有一部分可以并行。framework在力所能及的范围里把这些自动处理了,不需要程序员
主动去研究,发现把某些deep in the stack的function call改成并行的,能提高运行效率,再花很多时间开多线程、refactor一堆代码、test。程序启动的时候丢一个
thread pool executor就能解决大多数问题,方便太多了。
【 在 bihai (学得不好) 的大作中提到: 】
: main:
: Run(){
: ...
: execute(阻塞式或者说是同步过程);
: A a;
: execute(a->work()); //此处也是阻塞式或同步过程
: ...
: }
: class A {
: void work() {
: ...................

c
chebyshev

性能,可靠度,至少要考虑两个维度。未来的维护啥的就不说了。
我认为任何人写个程序都要考虑这两点。
因为即使是matlab简单的大白话,也可以有可靠度问题。

要什么可靠度的程序,就有什么样的写法。
这是没有一定之规的。此类问题在工业控制与嵌入式很常见。
未必是应用app之场景。

他说的逻辑其实很简单,说穿了就是个goto问题。
小学数学都用不到。但是要加起wrapper来,那学问就大了。

【 在 fantasist (一) 的大作中提到: 】
: 代码全用coroutine的好处是,比如main::Run()里第一个execute和第二个execute也许
: 有一部分可以并行。framework在力所能及的范围里把这些自动处理了,不需要程序员
: 主动去研究,发现把某些deep in the stack的function call改成并行的,能提高运行
: 效率,再花很多时间开多线程、refactor一堆代码、test。程序启动的时候丢一个
: thread pool executor就能解决大多数问题,方便太多了。

bihai

我们不需要并行,就是顺序执行。本来都是磁盘操作,都是在等待IO。

【 在 fantasist (一) 的大作中提到: 】
: 代码全用coroutine的好处是,比如main::Run()里第一个execute和第二个execute也许
: 有一部分可以并行。framework在力所能及的范围里把这些自动处理了,不需要程序员
: 主动去研究,发现把某些deep in the stack的function call改成并行的,能提高运行
: 效率,再花很多时间开多线程、refactor一堆代码、test。程序启动的时候丢一个
: thread pool executor就能解决大多数问题,方便太多了。

h
haidafu

" If your program needs to handle thousands or tens of thousands of
connections at a time, dealing with tens of thousands of threads will not be as efficient as trying to have only a few threads per CPU."

“efficient”这里怎么measure? 还是线程多,导致CPU消耗大,然后latency增加 (如果相关的stack在critical path上)?

看过一些优化thread pool的工作:减少capacity, 从而减少context switch和spin
lock,达到CPU优化的目的。

【 在 lestrois2000 (lestrois2000) 的大作中提到: 】
: 共同探讨。你的链接的帖子就挺好的,基本上解释了为什么需要async,以及async方案
: 从用户自己负责轮询到操作系统帮助轮询的演化。帖子的目的是推销它的Libevent
lib
: ,但是在之前他对这个问题的来龙去脉讲得很清楚。对于你的问题的重点看这段话:: ```
: (之前给出了多线程解决阻塞问题的代码)
: So, do we have the perfect solution for handling multiple connections at
: once? Can I stop writing this book and go work on something else now? Not : quite. First off, process creation (and even thread creation) can be
pretty
: expensive on some platforms. In real life, you’d want to use a thread
pool
: instead of creating new processes. But more fundamentally, threads won’t : ...................

l
lestrois2000

内存占用大。还有OS层面的epoll轮询在许多场合可以帮助I/O的zero copy提高性能。
多线程的方式一般总是要让数据写入用户空间的,有一个内存态到用户态的拷贝过程。而epoll实现的async可以使数据在内核态共享给用户,实现零拷贝,如果只是纯粹在I/O层面的读写数据而没有CPU参与进行数据计算的操作,比如反向代理Nginx。
CPU的利用率没有明显不同。

【 在 haidafu (海大富) 的大作中提到: 】
: " If your program needs to handle thousands or tens of thousands of
: connections at a time, dealing with tens of thousands of threads will not be
: as efficient as trying to have only a few threads per CPU."
: “efficient”这里怎么measure? 还是线程多,导致CPU消耗大,然后latency增加 (
: 如果相关的stack在critical path上)?
: 看过一些优化thread pool的工作:减少capacity, 从而减少context switch和spin : lock,达到CPU优化的目的。
: lib
: pretty
: pool