什么是线程?

什么是线程?这个问题很简单,但是想要回答好,也挺难。

 

《代码大全》里面非常推崇隐喻这种方式来介绍事物。隐喻有好处,可以让读者基于已有的知识背景来构建对新事物的认知。但是,这也仅仅是第一步,如果深入了解,往往需要挣脱固有认知的束缚。所以,隐喻的方式很容易让读者根据第一印象就将新事物与旧知识绑定在一起,而且十分紧密。事物之间,往往表象上接近,但是在本质上,却经常差别很大。这很正常,就如同,我们可以毫不费力的知道坐在旁边的是一个人,而非其他,因为他跟我们接近,但是他除了表面上的接近之外,内在的,肯定跟我们不一样。所以,隐喻有助于我们初次理解,打个照面,但是对于深入理解,往往造成一定的阻碍。

 

就如对于线程的了解,感觉经历了“看山是山,看水是水;看山不是山,看水不是水;再到看山还是山,看水还是水”的过程。那么,我就来慢慢说说这个过程。

 

在Jeffrey Richter写的《CLR via C#》中,有一段记忆深刻的表述,里面讲到“线程可以理解为逻辑的CPU”。感觉有点醍醐灌顶的意思,就如同混沌中的一个光点,有种“哎,是那么个意思”。但是想讲,跟本说不清楚。

 

最近自由时间比较多,所以抽出时间,专门对线程做了一些了解。慢慢来,看看能否说得清,毕竟“道可道,非常道”。

 

在我的理解里面,有一个场景能较大程度上对线程做一个描述,但也是较大程度上,如果深究,就显得靠不住了,所以,权当一个引子。

 

当我们启动一个应用程序时,操作系统会创建一个进程。先说下进程。进程是一个抽象的概念,如果落到实际物理意义上,简单可以理解成内存中的一段连续地址空间。当应用开始运行,首先需要有地方来存储它运行中需要的资源,比如加载的代码、创建的对象、打开的网络连接等。而且需要一个边界,这些资源不能跟其他应用程序的资源混在一起,否则,任何一个被多个应用程序使用的资源产生问题,那么相关多个应用程序都会出现问题。编程时,自身的bug已经疲于应付,如果还要为诸多未知情况伤神,那么程序员不是一个可以从事的职业。所以,我们尽量摆脱这种情况的干扰。操作系统也担心将它跟我们的应用程序混在一起,要不它自己也时时面对这种风险。

 

所以,操作系统在一个程序启动时,会单独给这个程序划分一段地址空间,比如从高位到低位2GB的内存空间(也并非实际这么大容量,有一部分映射到硬盘上,这个就不深入说了,大家网上了解以下即可),这段空间就是该应用运行时的独享空间,具有明显的边界,操作系统来保证其他的应用程序不会跨越这个边界。那么鉴于此,我们的隐喻来了。

 

我们将应用程序运行时所需的空间理解为生产车间厂房,厂房也是有边界的,就是四周的墙壁和头顶的房顶。那么就让我们来看看厂房里面有什么。首先,厂房里面堆积着各种生产所需的材料,存放的位置也是固定下来的,不会随意堆放,甚至严格到什么类型的资源必须放到那里;然后,还有生产线,一条条的生产线将资源一步一步转换到产品。生产线上还有工人,不停处理生产线上的资源。

 

下面,通过这个隐喻,我们来探讨几个问题。

 

1、隐喻中,概念之间的对应关系是怎样的?

进程 -> 厂房

线程 -> 生产线

资源 -> 生产材料

工人 -> CPU

 

2、进程和线程的关系是怎样的?

进程 = 线程 + 资源。这里面没有CPU的,CPU不是进程独有的。

 

3、线程是什么?

好,重头戏来来,线程到底是什么?先说说我之前的理解吧,懵懂时,我将线程理解为一个实际的概念,结合上面的隐喻,就是一个生产流水线,然后掉进去谁来驱动这个流水线运转的,再到后来是线程是如何实现阻塞的,又是怎么醒过来的?想不通啊。后来,看书、看代码、写代码,逐渐有点认识,线程跟流水线并不是对等的概念,线程应该是流水线上的流水,也就是步骤。举Java中的一个证据,在实例化一个线程的时候,两种方式,一个是重写Thread类的Run()方法,一种是实现一个Runnable接口的实例,然后将实例传递给Thread的构造函数。我们创造线程,不就是来执行Run()或者Runnable里面的逻辑吗?那么根源上理解,是不是线程就是包装了一串执行逻辑呢?大家体会一下。

 

将线程理解为一组执行逻辑非常关键。我们的应用程序是不是一大组执行逻辑?是的,应用程序从启动之时,不就是按照我们编排的路线在执行吗?那为什么需要多线程呢?现在先按下不表,要么这个问题就太长了。

 

4、线程跟资源的关系

这个问题呢,看似很简单,但是很基础,首先,线程和资源是平等的关系,虽然资源都是在处理逻辑过程中创建的,也会在线程执行过程中终结资源,但是要清楚知道,资源在整个进程(厂房)内都是共享的,就跟上面的隐喻一样,它存放在所有处理逻辑(线程)都能看得见的地方,想用的话,就可以直接申请去用,但是需要遵从一定的规矩。这个就是锁产生的根本原因。

 

5、应该如何理解“线程就是逻辑的CPU”?

这句话不能说错,在J.R的行文中,这样说有助于让读者理解,但是也会造成实质的误解。最终的执行逻辑,大家都知道,是CPU来完成的,这样类比,从执行角度上看没问题。但是毕竟是两个概念。应该这样理解,线程(任务序列)准备好,CPU在合适的时间就过来执行一下。还是隐喻的例子,车间内有10条生产线流程(线程),但是只有两个工人(CPU),为了让10条生产线上的工作都能推进,两个工人约定好,在每个生产线上干10分钟,就切换到下个生产线上。这是从线程的角度说是CPU切换到了下个线程,其实这是与实际情况正好相反的。操作系统底层是执行了一会这个线程,就让这个线程下CPU,让另一个线程上CPU,是需要从CPU的视角看待这个过程的。

 

通过上面的隐喻和问题,不知道大家了解了线程没有。

线程本质上就是一组任务执行序列,但是这么抽象的概念,如何让CPU或者编程人员操作呢,这个时候就从操作系统层面,给分配了一个对象,Thread或者Process,来与一个线程关联。关联之后,就可以用这个对象的一些属性来标志线程的属性,就抽象的线程具象化,比如线程叫什么名字,优先级是多少,是否为后台线程,当前的状态是什么样子的,如果从抽象的任务队列上来说这些概念,是没办法说清楚的。

 

大家需要深刻认识,线程就是一组逻辑上的任务执行序列,就想菜谱书上的菜谱,一步一步照做就可以了。菜谱上需要的材料,在执行菜谱前,需要准备好,否则没办法做菜。谁来炒菜,那肯定是厨师了(CPU)。如果家里有两个炉子,两个厨师可以一起炒菜,肯定比一个要快。这个时候,两个厨师可以都需要同一个材料,比如盐、胡椒粉,不能你去拿我也去拿,得你用完我再用。这是又一个隐喻。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值