进程 VS 线程

一、进程和线程

1.1 概念

?网上看到了一篇比较好的解释,从计算机的发展角度来分析进程和线程的。

早期的计算机是没有线程这么一说的,进程就是计算运行的基本单位,包含静态的资源和动态的计算。随着计算机性能的提升和系统设计的改进,为了避免进程之间的调度带来的开销,同时提升系统的并发性,于是在进程中引入了线程的概念,专门用来负责程序的动态部分。

线程专门用来描述系统的动态计算,是系统调度和运行的基本单位,而现在的进程已经是一种静态的概念,可以理解为为一个或者多个线程提供共享资源的上下文容器,更多强调的是系统的资源,不在强调动态性,这些动态特性已经被转移到线程身上。

  • 进程

在一定环境下,把静态的程序代码运行起来,通过使用不同的资源来完成一定的任务,这些资源可以成为计算机执行的上下文环境,进程是资源分配的基本单位,强调的是内存资源的管理。

  • 线程

线程是进程的一部分,扮演的角色是在进程已经掌握的资源下怎么利用CPU去运行代码和动态计算,这里牵扯到的是CPU,寄存器和线程的栈。线程是CPU轮流调度的基本单位,强调的是CPU的运行。

总结起来,就是进程是资源分配的基本单位,而线程是CPU在进程内切换的基本单位,线程属于进程。

1.2 进程和线程的区别

这里举个栗子做类比,进程=火车,线程=车厢

  • 线程在进程下运行(单纯的车厢无法运行)
  • 一个进程可以包含多个线程(一辆火车包含多节车厢)
  • 不同进程间共享数据很难(一辆火车的乘客很难跑到另外一辆火车)
  • 同一进程下的不同线程数据很容易共享(A车厢的乘客很容易换到B车厢)
  • 进程要比线程消耗更多的资源(火车比单节车厢消耗的资源更多)
  • 进程之间不会相互影响,一个线程挂掉将导致整个进程挂掉(一列火车不会影响另外一辆火车,但是如果一辆火车上的某节车厢出故障了,将影响整列火车的运行)
  • 线程使用的内存地址可以上锁,即一个线程使用某些内存的使用,其他线程必须等待它结束才能使用(火车上的洗手间一次只能一个乘客使用)
  • 进程使用的内存地址可以限定使用量——信号量(比如火车上的餐厅,最多只允许多少人进去,如果满了需要在门口等待)
  • 进程可以拓展到多级,使用多核

二、多进程和多线程

?这里参考廖雪峰老师的博客,讲的特别好。

2.1 概念

多进程和多线程都是多任务处理。要实现多任务,我们通常会设计Master-Worker模式,即Master负责分配任务,Worker负责执行任务,通常情况下,一个Mater、多个Worker

  • 多进程:Master为主进程,Worker为其他进程
  • 多线程:Master为主线程,Worker为其他线程

多进程的python代码

import multiprocessing as mp


def fun(a):
    print(a)


if __name__ == '__main__':
    # 这里有三个任务,手动指定3个进程
    p1 = mp.Process(target = fun, args=(1,))
    p2 = mp.Process(target = fun, args=(2,))
    p3 = mp.Process(target = fun, args=(3,))
    p1.start()  # 启动进程
    p2.start()
    p3.start()
    p1.join()  # 等待子进程执行完毕
    p2.join()
    p3.join()

输出结果

1
2
3

多线程的python代码

import time, threading

# 新线程执行的代码:
def loop():
    print('thread %s is running...' % threading.current_thread().name)
    n = 0
    while n < 5:
        n = n + 1
        print('thread %s >>> %s' % (threading.current_thread().name, n))
        time.sleep(1)
    print('thread %s ended.' % threading.current_thread().name)


if __name__ == '__main__':
	print('thread %s is running...' % threading.current_thread().name)
	t = threading.Thread(target=loop, name='LoopThread')
	t.start()
	t.join()
	print('thread %s ended.' % threading.current_thread().name)

输出结果

thread MainThread is running...
thread LoopThread is running...
thread LoopThread >>> 1
thread LoopThread >>> 2
thread LoopThread >>> 3
thread LoopThread >>> 4
thread LoopThread >>> 5
thread LoopThread ended.
thread MainThread ended.

2.2 多进程和多线程的区别

  • 多进程的最大优点是稳定性高,因为一个子进程崩溃,不会影响主进程和其他子进程
  • 多进程模式的缺点是创建进程的代价大,在Unix/Linux系统下,用fork()调用还行,但是在Windows下创建进程开销巨大,另外,操作系统同时运行的进程数有限,在内存和CPU有限的情况下,如果有几千个进程同时运行,操作系统连调度都成问题。
  • 多线程模式通常比多进程快一点,但是也快不到哪里去,而且多线程致命的缺点是任何一个线程挂掉可能导致整个进程崩溃,因为所有的线程共享进程的内存。

2.3 线程切换

无论是多进程还是多线程,只要数量一多,效率肯定上不去,为什么呢?

我们打个比方,假设你不幸正在准备中考,每天晚上需要做语文、数学、英语、物理、化学这5科的作业,每项作业耗时1小时。

如果你先花1小时做语文作业,做完了,再花1小时做数学作业,这样,依次全部做完,一共花5小时,这种方式称为单任务模型,或者批处理任务模型。

假设你打算切换到多任务模型,可以先做1分钟语文,再切换到数学作业,做1分钟,再切换到英语,以此类推,只要切换速度足够快,这种方式就和单核CPU执行多任务是一样的了,以幼儿园小朋友的眼光来看,你就正在同时写5科作业。

但是,切换作业是有代价的,比如从语文切到数学,要先收拾桌子上的语文书本、钢笔(这叫保存现场),然后,打开数学课本、找出圆规直尺(这叫准备新环境),才能开始做数学作业。操作系统在切换进程或者线程时也是一样的,它需要先保存当前执行的现场环境(CPU寄存器状态、内存页等),然后,把新任务的执行环境准备好(恢复上次的寄存器状态,切换内存页等),才能开始执行。这个切换过程虽然很快,但是也需要耗费时间。如果有几千个任务同时进行,操作系统可能就主要忙着切换任务,根本没有多少时间去执行任务了,这种情况最常见的就是硬盘狂响,点窗口无反应,系统处于假死状态。

所以,多任务一旦多到一个限度,就会消耗掉系统所有的资源,结果效率急剧下降,所有任务都做不好。

2.4 计算密集型与IO密集型

是否采用多任务的第二个考虑是任务的类型。我们可以把任务分为计算密集型和IO密集型。

  • 计算密集型任务

计算密集型任务的特点是要进行大量的计算,消耗CPU资源,比如计算圆周率、对视频进行高清解码等等,全靠CPU的运算能力。这种计算密集型任务虽然也可以用多任务完成,但是任务越多,花在任务切换的时间就越多,CPU执行任务的效率就越低,所以,要最高效地利用CPU,计算密集型任务同时进行的数量应当等于CPU的核心数。

计算密集型任务由于主要消耗CPU资源,因此,代码运行效率至关重要。Python这样的脚本语言运行效率很低,完全不适合计算密集型任务。对于计算密集型任务,最好用C语言编写。

  • IO密集型任务

第二种任务的类型是IO密集型,涉及到网络、磁盘IO的任务都是IO密集型任务,这类任务的特点是CPU消耗很少,任务的大部分时间都在等待IO操作完成(因为IO的速度远远低于CPU和内存的速度)。对于IO密集型任务,任务越多,CPU效率越高,但也有一个限度。常见的大部分任务都是IO密集型任务,比如Web应用。

IO密集型任务执行期间,99%的时间都花在IO上,花在CPU上的时间很少,因此,用运行速度极快的C语言替换用Python这样运行速度极低的脚本语言,完全无法提升运行效率。对于IO密集型任务,最合适的语言就是开发效率最高(代码量最少)的语言,脚本语言是首选,C语言最差。

参考文献

  • 汤子瀛.计算机操作系统(第三版).2007:34-78
  • https://www.jianshu.com/p/5ce909404c90
  • https://www.liaoxuefeng.com/wiki/1016959663602400/1017631469467456
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值