原创:小姐姐味道(微信公众号ID:xjjdog),欢迎分享,转载请保留出处。java
做为一只鸟,能够边吃东西边拉屎么?这对一个养过鹦鹉的人来讲,答案是确定的。程序员
从鸟嘴到鸟大肠,整个过程是串行的。虽然有些曲折,但方向是单一的,出口是必定的。只要鸟可以克服一些心理上的不悦,它就能办获得。bash
这种状况在一些原始的物种身上显得有些尴尬。好比水螅,它属于腔肠动物,无性生殖。这几个单词都挺唬人,但它的体内只有一个空腔。从哪里吃,就从哪里出。微信
同是地球上的物种,从长远看来,咱们仍是它的近亲。多线程
做为高级哺乳动物,咱们可以在呼吸的时候,同时说话,也可以同时听到声音,看到赏心悦目的风景。若是你想的话,也能够办到鹦鹉作的事。架构
这些信息被收集以后,一股脑发送给大脑进行处理。有多是像深度学习同样,存了一堆权重,但这些信息究竟是如何处理的,咱们如今还不得而知。并发
因此程序员们转而研究计算机,毕竟这个相比起“最后归途是哲学”的人类大脑,就像是个玩具。高并发
咱们都知道,干一堆事干很差,不如集中精力把一件事干好。这并非说一我的没能力把全部的事情干好,而是在不一样的事务之间切换,是要耗费资源的。学习
你的大脑好不容易熟悉了一个工做场景,结果忽然调度给它另一项任务,它就要花很长时间切换到新的工做场景中。this
有时候代码写多了,我就连说话都开始口吃。但一直不停的说说说,就又恢复了。
因此,全部的人都恨零零散散的工做事务。尤为是恨哪些不断给你小事情,但又绝不相关的任务的领导。
到头来,感受作了不少,但一点成果都没有,感受人都废了。
不要怕,咱们看看CPU是怎么处理的。
CPU处理任务时不是一直只处理一个,而是经过给每一个线程分配CPU时间片,时间片用完了就切换下一个线程。
时间片很是短,通常只有几十毫秒,CPU经过不停地切换线程执行,但咱们几乎感受不到任务的停滞。由于对人类来讲,高质量的游戏,每秒只须要60帧,就算是流畅了。
这个时间仍是至关可观的,特别是在进程上下文切换次数较多的状况下,很容易致使CPU将大量时间消耗在寄存器,内核栈以及虚拟内存等资源的保存和恢复上,进而大大缩短了真正运行进程的时间。
对于Linux来讲。程序在执行过程当中一般有用户态
和内核态
两种状态,CPU对处于内核态根据上下文环境进一步细分,所以有了下面三种状态:
咱们看一下Linux的top命令,是怎么显示内核态和用户态的。
如上图,us就是user的意思;sy就是system的意思。分别表明了用户态和内核态。
若是sy占用的过高,就有多是上下文切换和中断太频繁了。
那什么是上下文?
所谓的上下文,说白了就是一个环境。好比你去食堂带着饭盒,去厕所带着厕纸。要是搞乱了,去厕所带着饭盒,感受上就不正常。操做系统为每个进程,分配了这么一个上下文,用来存放:代码、数据、用户堆栈、共享存储区、寄存器、进程控制块等。
先不要管里面的细节了,反正内容不少,切换确定是要有陈本的。好比,厕纸放在家里卧室柜子的第三层小隔间。
vmstat命令显示的这几列,就是这么个意思。cs不是csgo的缩写,它的全拼是context switch
。
在每一个进程里,也能够看到累加的值。
[root@localhost ~]# cat /proc/2788/status
...
voluntary_ctxt_switches: 93950
nonvoluntary_ctxt_switches: 171204
复制代码
cs若是过高,那就是线程或者进程开的太多了。
上下文切换又分为2种。
让步式上下文切换和抢占式上下文切换。
下面先说下让步式上下文切换。咱们拿Java中的cas操做来看就能够了。
cas除了 compare and switch
原始指令支持之外,还须要一个循环来保证。
public final long getAndAddLong(Object var1, long var2, long var4) {
long var6;
do {
var6 = this.getLongVolatile(var1, var2);
} while(!this.compareAndSwapLong(var1, var2, var6, var6 + var4));
return var6;
}
复制代码
代码放在循环里,在并发量比较高的状况下,若是许多线程反复尝试更新某一个变量,却又一直更新不成功,循环往复,会给CPU带来很大的压力。因此,让步式上下文切换,是指执行线程主动释放CPU,与锁竞争严重程度成正比,可经过减小锁竞争来避免。
而抢占式上下文切换,是指线程因分配的时间片用尽而被迫放弃CPU,或者被其余优先级更高的线程所抢占。通常因为线程数大于CPU可用核心数引发,可经过调整线程数,适当减小线程数来避免。
那为啥Java的线程就可以比多进程速度快一些呢?由于Java的线程本质上也是一种轻量级进程,但它的虚拟内存等信息是共享的,只须要切换线程的私有数据,寄存器等不共享的数据。即便这样,也会耗费很多时间。
使用perf命令一样可以观测到这个上下文切换到过程和数量。好比:
# 跟踪全部上下文切换,直到Ctrl-C:
perf record -e context-switches -c 1 -a
# 包括使用的原始设置(请参阅:man perf_event_open):
perf record -vv -e context-switches -a
# 使用堆栈跟踪的示例上下文切换,直到Ctrl-C:
perf record -e context-switches -ag
复制代码
使用perf report
便可查看相关结果。
对于计算机来讲,效率最高的依然是专心作一件事。必定程度上,你也算是计算机的老板。若是你一直让它干一些杂活,把它当牛使,那你的计算机效率不必定会高。
有些人很聪明,他必定知道这种来回切换的方式对你的工做效率影响巨大。排除他愚蠢的属性,就只剩下坏:给你一堆烂七八糟的事,搞得你身心疲惫,最后又和你讲结果导向的---必定是你的领导故意为之。
做者简介:小姐姐味道 (xjjdog),一个不容许程序员走弯路的公众号。聚焦基础架构和Linux。十年架构,日百亿流量,与你探讨高并发世界,给你不同的味道。个人我的微信xjjdog0,欢迎添加好友,进一步交流。