前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >传统功夫这叫化劲儿 --- 多线程系列(一)

传统功夫这叫化劲儿 --- 多线程系列(一)

作者头像
老李秀
发布2021-06-17 20:24:17
4220
发布2021-06-17 20:24:17
举报
文章被收录于专栏:可能是东半球最正规的API社区

大家好,我是谢顶道人老李,多日不写文字显得颇为生疏、显得颇为江郎才尽、显得颇为文案匮乏。

早在去年上半年,老李借助于疫情带来的公共划水时间给大家带来划水文章系列集合---《PHP网络编程》,一定程度上涉及到了较为初级入门的多进程编程,然而作为一个偏向系统编程的系列,竟然没有涉及到多线程实在是说不过去,这个事情是这样的...

有两个年轻人,一个秃头法师、一个无发坦克,来找我说:李老师,我一天天在公司复制粘贴,事业没有进步、技能没有长进、面试天天吃屎,你能不能教教我混鱼功法,帮助治疗一下难题。

我说:年轻人你在公司天天练死劲儿,不好用。

他不服气。

我说:小朋友,我给你两个键盘+鼠标,来对付我一个键盘+触摸板,你粘不过我,因为我精通多线程。

塔们说:你这多线程没用。

我说:我这有用。这就是研发效率,传统研发是讲效率的,当年东软几百人都粘不过我一个人。

塔们说:没用的,我们用php-fpm。

... ...小伙子,你这TMD不讲武德啊!

也不是我不想写多线程,主要是PHP是真没有多线程(包括那个pthread扩展),不然也不至于用脸去接Java的拳。然而在真正开始复制粘贴多线程之前,得先说好了:实际上一开始UNIX/Linux也没有多线程,为了实现多任务并发执行都是一群人弄多进程猛堆,场面一度令人不堪入目、十分恶心。

一些人恶心的受不了了,于是开始自己搞简易版本的线程实现,当然了这种情况下搞出来的线程实现一定都是纯用户态的,属于拿芭蕉叶子当裤衩子的临时行为。再到后来POSIX也实在恶心的受不了了,于是在1995年提出了一个POSIX-Threads标准(这东西简称就是你所能经常见到的Pthread),然后就有人开始在Linux/UNIX去实现这个标准。

其中比较典型的就是LinuxThreads,其作者是Xavier.Leroy,这玩意我是没用过,我也是从一些小黄书中寻得其一些蛛丝马迹。这玩意本质上是利用clone系统调用去实现的,由于是利用了内核去产生并且实现调度,所以LinuxThreads算是一个内核级线程的实现,但是尽管如此,TA还是具备了不少被历史潮流不得不替换的典型缺陷,如果你非让我列举一下原因那就比如说:由于其打法比较落伍,无法使得自身形成闭环,在多平台系统中缺少发力点,难以提升用户转化率,且由于自身实现依赖clone很容易遭到友商降纬打法,虽然一定程度上解决了用户痛点,但是在敏捷逻辑布局的今天,难以沉淀形成矩阵力量... ...(注PS:由于LinuxThreads是满足了部分POSIX-Threads标准的,所以这东西在BSD上也是可以用的)

总之就是不好,憋问太多,问就是薛定谔的线程。在LinuxThreads不久之后,就先后有两个大型公司先后参与了线程实现的研发,一则是来自于IBM的NGPT工程,另一个是来自于RedHat的NPTL工程,这两家公司刚开始都是打着提升LinuxThreads的口号而来的友商,你搞你的我搞我的,反正科研经费是给足了,双方各自干的热火朝天,结果后来搞着搞着不知道咋回事就搞到一起去了,再到后来搞着搞着IBM干脆就把自己NGPT搞没了,大概意思就是说:我们NGPT的精华已经被注入到了NPTL中去了,二者精华?已融合。而对于RedHat而言,反正自家系统用户有的是,索性直接把接受了NGPT精华的NPTL一波儿干到了自家的RedHat Linux中去了。再往后发展就顺理成章被集成到了Linux 2.6内核中去了,自此算是实现了真正的内核态线程支持。

总结一句话:Pthread是标准,而无论是LinuxThreads还是NGPT还是NPTL这都是具体的实现,总之就是标准与实现的故事。

实际上在有线程之前,哪怕是用户自己搞出来了自己的山寨线程,操作系统的基本调度对象与资源分配对象依然还是进程,进程是OS的最小执行单位。而一旦有了内核级线程,线程则为OS最小执行以及资源调度单位。在有线程这个概念之前,来举个栗子说明下进程的难言之隐,那真的是TA不好程序员也不好。

你在用游戏直播软件X一起视频开黑,由于涉及到了打字、视频与语音三件事,为了保证这三件事儿能同时进行X直接fork了三个进程来分别做这三件事儿,然后CPU会按照调度算法不断去轮这三个进程,每轮一个都要保存上下文与恢复上下文以及切换为响应的虚拟内存空间,不过这都小事儿,毕竟还能解决;最恶心的事儿是:处理视频聊天的进程独占了摄像头这个IO设备,如果碰巧语音那个进程由于迫不得已的原因也要使用一下摄像头资源,这就有点儿恶心了。

如果说有了多线程,那么软件X则不必一次fork出三个进程去搞这件事儿了,一个进程中劈叉出三个线程去做就可以了,而且三个线程之间切换任务是在同一个进程内部无需进程切换,最大的优势就是摄像头资源是共享的,你用我用大家用,算是尽最大程度实现物尽其用。

所以说线程出现的必然性是因为进程的两个主要缺点导致的:

一、进程之间彼此独立自治

二、进程之间切换代价略大

在当前主流的线程设计方案中,按照线程模型这个维度去分类的,可以有三大类:

一、1:1模型。一个用户态线程对应一个内核态线程,就是你用pthread_create创建一个线程,那么会产生一个用户态线程与一个与之对应的内核态线程。这是最粗暴最爽的实现方式,早期Win32以及Linux(NPTL)都是这么干的。这种情况下,如果线程A遇到IO阻塞,那么操作系统就可以操作将其状态转换为阻塞态然后将CPU分配给同个进程下处于ready状态中的其他线程。这种方式最大的优点就是将并发性能提升到了极致,而这种方式的最大缺点就是线程切换时候需要从用户态内陷到内核态才能完成。

二、N:1模型。这种情况下你创建了N个线程,这N个用户态线程只有一个内核态线程与之对应,这种情况就基本上就是退化为纯用户态线程了。如果此时多个线程中有一个线程遇到IO阻塞,由于内核态线程只有一个,OS无法调度其他线程所以由于一个线程的阻塞将会导致整个进程的阻塞,但是这种模型最大的优点就是多个用户态线程之间的切换不需要系统内核干涉,是纯用户态完成。

三、M:N模型。结合一和二优势,你们感受一下。

懵了是么,怎么又是内核态线程又是用户态线程,难道是同时存在的?其实这个问题会搞懵很多人。我们再回到开头去捋一捋这件事情就行了:

1、很久之前,系统只有进程没有线程

2、后来不久,开发者自己搞山寨版黑线程,系统内核对此一所所知

3、再到后来,大家都觉得太恶心了于是提标准搞实现,标准叫做Pthread,实现分别有LinuxThreads、NPTL等等,这会儿有了内核态线程,操作系统内核已经对线程有了感知,线程成为最小调度单位

而从我个人使用和理解看来,如何理解NPTL这种1:1概念呢,实际上这件流程完整的完成本身是由是由内核态和用户态一起协作才能搞定的事情,其次是对于“ 用户态线程 ”你要理解为这是工程代码的载体单位,对操作系统透明,而与之通过某种比例对应的“ 内核态线程 ”则是操作系统调度最小基本单位,是获取CPU资源的载体单位。

当用户通过pthread_create()函数创建一个线程后,在用户态层次为了能够让用户操作该线程所以将线程信息保存在了pthread相关struct中。而系统为了能够调度该线程(既内核态线程),则在内核态下同样适用task_struct与clone创建一个结构来管理调度。注意:对于Linux而言,进程和线程都是通过task_struct来表达的,只不过线程是通过浅拷贝,多个线程时间共享内存等相关数据,线程对于Linux而言可以理解为一种特殊的进程。

好了,周五了我快顶不住了,你们好好,我也好好的,休息一下,下篇开始一起粘代码了。

LinuxThread:https://web.archive.org/web/19961128030318/http://pauillac.inria.fr/~xleroy/linuxthreads/

IEEE 1003.1c:https://standards.ieee.org/standard/1003_1c-1995.html

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-05-14,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 高性能API社区 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
云直播
云直播(Cloud Streaming Services,CSS)为您提供极速、稳定、专业的云端直播处理服务,根据业务的不同直播场景需求,云直播提供了标准直播、快直播、云导播台三种服务,分别针对大规模实时观看、超低延时直播、便捷云端导播的场景,配合腾讯云视立方·直播 SDK,为您提供一站式的音视频直播解决方案。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档