博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
浅谈线程和进程
阅读量:3934 次
发布时间:2019-05-23

本文共 3480 字,大约阅读时间需要 11 分钟。

线程和进程是程序员老生常谈的问题了,任何阶段的程序员都不敢轻视他。

事实上大部分程序员并没有系统化的学习过,也有很多人并没有机会好好运用它。所以,如果拉一个工作多年的程序员讨论,对方未必能说出个所以然。

本文是 Linux 下 C++ 多线程编程开发的系列文章之首,在介绍具体编程实现而言,先讲讲它的基础概念,并给予通俗化的解释,并在文章最后给出一个开放的思考题。

什么是线程?

线程(英语:thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。1

上面的定义来自于百度百科,定义的很准确,但同时也很抽象。

线程可一看作是轻量级的进程,它依赖于进程。

所以,搞清楚线程前,我们先来看看进程是什么。

什么是进程?

进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。2

上面的定义同样来自于百度百科,定义非常准确,但同时也非常抽象。

所以,为了很好的搞明白线程和进程,我尝试用一些通俗的比喻来解释。

我们把整个计算机比喻成一个公司,公司由一些基本的单元组成:

软硬件设备资源部门员工制度流程

公司要正常运作,依赖于制度流程。

制度流程可以等同于什么呢?

操作系统

正因为规章制度流程的存在,公司的软硬件、设备资源可以协调给每一个部门。

如果公司十分庞大,组织复杂,那么部门就是最基础的单元。

也就是说

进程可以看做是部门

部门依法使用公司规定的软硬件资源,进程在操作系统的只能也是类似。

上面的定义中有讲到,现代操作系统中,进程是一个容器。

在这里插入图片描述

每个进程拥有自己的独立空间,相互间不干扰。

这个好理解,现实中,每个部门有自己的软硬件和员工,一般情况下相互不干涉。

那么线程呢?

进程和线程有什么区别?

线程依赖于进程,而不能独立存在。

线程是最基本的执行单元,对比现实生活就是是部门中的员工。

仔细体会一下。

部门提供资源,员工干活。

进程提供资源,线程干活。

但如果公司有重大决定,它一般不会直接针对个人,而是下发到部门,控制部门这一层就好了。

如果操作系统根据事情轻重缓急,它也会直接和进程交涉,进程受它的调度。

一个进程一般有一个或者多个线程。

同一个进程中的线程可以共享进程的数据。

但同时,其实线程也有自己的内存模型。

线程开发中可以利用 TLS(Thread-Local Storage,线程局部存储)来实现线程独有的内存数据不被同一个进程其它线程干扰,每一个线程维护一份共享变量的拷贝,所以基于拷贝上的操作不会影响其它线程。

那么,线程和进程有什么区别呢?

进程是资源分配的基本单位,线程是调度的基本单位。

这是一句名言,很好地概括了两者的区别。

用一句话来概括就是:

进程对应操作系统,线程对应 CPU。

我们常说的任务调度,其实通常讲 CPU 通过时间片轮转,调度线程。

也并不是说进程不能调度,是说线程更轻量化。

如果要了解更深入的话,可以带入到下一个问题。

什么时候用线程和进程?

我们用线程,提起的很多的就是多线程。

多线程的目的就是并行开发。

大家讲多线程的时候,总喜欢讲车站买票的例子。

窗口:@-@-@-@-@-@-@-@-@-@

1

现在有 10 个人买票。

如果这个工作效率很低,那么我们可以这样:

窗口:@-@-@-@-

窗口:@-@-

窗口:@-@-@-@-

12345

增加类似的窗口,提供并行服务。

并行的目的是为了加速。

多线程开发也是基于上面 2 个原因:

并行加速

并行就是,我要一边听歌,一边写文章。

加速就是,下载一个文件单线程太慢了,多个线程一起下载就能加速。

那是不是线程越多越好呢?

答案是否定的。

多线程能干的事情多进程也能干。

但两者都不是越多越好。

这涉及到上下文切换的问题。

上下文切换

上下文的英文单词是 Context。搞 Android 和 Java Web 开发的应该再熟悉不过了,它代表的是任务的同一语境。

上下文切换就是任务切换,可能是进程的切换也可能是线程的切换。

上下文的切换是一个很深的话题,我们大可不必在此过多讨论,我们可以简单看待:

上下文的切换就是任务状态的保存与恢复。

当 CPU 挂起一个任务时,它需要将 CPU 寄存器里面的信息保存到任务的堆栈当中,然后从下一个任务的堆栈中读取对应的 CPU 寄存器信息并恢复,那么下一个任务就可以执行了。

在这里插入图片描述

图片来源于网络3

但上下文切换的开销很昂贵,它有大量的工作需要处理,比如寄存器和内存页表的存储和恢复、内核数据结构的更新等等。

所以,线程并不是越多越好,因为线程越多,上下文切换越频繁,极端情况下效率不升反降。

并且,进程的上下文切换比线程开销的要大。

注意:在同一个进程中,线程的切换不会引起进程的切换,不同进程中,线程的切换会引起进程的切换。

所以,回到问题什么时候用进程什么时候用线程这个问题上来,一般认为:

相互独立的任务用进程因为内存独立的原因,资源相互不干扰,比如播放音乐用一个进程,图像绘制用一个进程。音乐出故障了,图像还能正常绘制。如果用线程的话,单线程发生故障可能导致整个进程一起被干掉。

2.相关性高的并行需求用线程

​ 因为线程上下文切换开销小,所以同一个任务或者同一个业务逻辑的代码可以尝试用线程开发。

线程开发的基础概念

线程和进程的基本差别上面内容介绍的差不多了,下面提一些线程开发中常出现的基础概念。

用户态线程和内核态线程

用户态线程指的是用户层面自己创建的线程,自己管理生命周期,包括创建、切换、销毁。比如,最近流行的协程就属于此,一般通过线程库实现。

内核态线程指的是由操作系统的内核管理生命周期的线程,如用 pthread 创建的线程。

POSIX 标准

POSIX(Portable Operating System Interface of UNIX)是可移植操作系统接口,定义了操作系统应该为应用程序提供的接口标准。

什么意思呢?

操作系统有很多种,有些编程语言可以跨平台开发,有些则不能。

比如,Android 成立之初就选用 Java,原因就是 Java 跨平台,所以那些做 Java 开发的工程师可以很快投入到新的领域。但是 Java 的跨平台是建立在虚拟机上,虚拟机屏蔽了操作系统的不同,提供了统一的 API,但一定程度上牺牲了性能。我们很多年感觉的 Android 卡,和这有很大关系。

而 POSIX 意在获得操作系统源码级的 API 支持。

并且,POSIX 标准中定义了进程和线程相关标准。

Linux 是支持 POSIX 标准的,我们用 pthread 创建线程就属于此。

线程库

在 Linux 开发中,线程的实现可以通过系统调用。

但系统调用太麻烦了,一般用线程的封装好的线程库,目前常用的有:

posix pthreadC++11 ThreadBoost 线程库

思考题

文章最后,抛出一个问题。

我们知道,多线程开发是为了并行。

并行是通过 CPU 时间片调度而来。

线程 A: ++ + + +

线程 B: - - – -
CPU : +±±±-±

1234

也有同学知道,所谓并行其实只是看起来并行。

那问题是:

可以串行的任务,非要人为并行吗?

引用

https://baike.baidu.com/item/%E7%BA%BF%E7%A8%8B/103101?fr=aladdin ↩︎https://baike.baidu.com/item/%E8%BF%9B%E7%A8%8B/382503?fr=aladdin ↩︎https://baike.baidu.com/pic/%E4%B8%8A%E4%B8%8B%E6%96%87%E5%88%87%E6%8D%A2/4842616/0/37d12f2eb9389b504fc24c9c577df2dde71191efc0ee?fr=lemma&ct=single#aid=0&pic=37d12f2eb9389b504fc24c9c577df2dde71191efc0ee ↩︎

转载地址:http://wbegn.baihongyu.com/

你可能感兴趣的文章
java 数组笔记整理
查看>>
java IO/NIO 下载上传的笔记
查看>>
对行为的描述---一般系统论读书笔记
查看>>
贪心算法
查看>>
分支限界法
查看>>
随机化算法
查看>>
项目整体管理(一)
查看>>
项目整体管理(二)
查看>>
推荐阅读书籍
查看>>
外包管理
查看>>
项目管理师职业道德规范
查看>>
战略管理概述
查看>>
业务流程管理和重组
查看>>
知识管理
查看>>
项目整体绩效评估
查看>>
信息安全系统和安全体系
查看>>
信息系统安全风险识别与评估
查看>>
信息安全系统的组织管理
查看>>
项目时间管理脉络
查看>>
项目成本管理脉络
查看>>