计算机系统

进程和线程

进程(Process)是资源分配的最小单位,具有一定功能的程序 关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位。每个进程都有自己的独立内存空间,不同进程通过进程间通信来通信。由于进程占独立内存,所以进程间的切换开销大。

线程(Thread)是进程的一个实体,是独立运行和独立调度的最小单位(CPU上真正运行的是线程)。

  • 线程之间的通信更方便,同一进程下的线程共享全局变量,静态变量等数据。而进程之间的通信需要以 通信的方式进行
  • 线程的调度与切换比进程快很多,同时创建一个线程的开销也比进程要小很多
  • 多进程程序更稳定,多线程程序只要有一个线程死掉,整个进程也死掉了。

任务可以理解为进程(process),如打开一个word就是启动一个word进程。在一个word进程之中不只是进行打字输入,还需要拼写检查、打印等子任务,我们可以把进程中的这些子任务称为线程(thread)。

由于每个进程至少要干一件事,那么一个进程至少有一个线程,有时候有的复杂进程有多个线程,在进程中的多个线程是可以同时执行的。多线程的执行方式和多进程是一样的,也是由操作系统在多个线程之间快速切换,让每个线程都短暂地交替运行,看起来就像同时执行一样。当然,真正地同时执行多线程需要多核CPU才可能实现。

总结一下就是,多任务的实现有3种方式:

  • 多进程模式;

启动多个进程,每个进程虽然只有一个线程,但多个进程可以一块执行多个任务。

  • 多线程模式

启动一个进程,在一个进程内启动多个线程,这样,多个线程也可以一块执行多个任务。

  • 多进程+**多线程模式。**

启动多个进程,每个进程再启动多个线程,这样同时执行的任务就更多了,当然这种模型更复杂,实际很少采用。同时执行多个任务通常各个任务之间并不是没有关联的,而是需要相互通信和协调,有时,任务1必须暂停等待任务2完成后才能继续执行,有时,任务3和任务4又不能同时执行,所以,多进程和多线程的程序的复杂度要远远高于我们前面写的单进程单线程的程序。


网上有些很形象的比喻:阮一峰的博客转载自知乎用户biaodianfu

做个简单的比喻:进程=火车,线程=车厢

  • 线程在进程下行进(单纯的车厢无法运行)
  • 一个进程可以包含多个线程(一辆火车可以有多个车厢)
  • 不同进程间数据很难共享(一辆火车上的乘客很难换到另外一辆火车,比如站点换乘)
  • 同一进程下不同线程间数据很易共享(A车厢换到B车厢很容易)
  • 进程要比线程消耗更多的计算机资源(采用多列火车相比多个车厢更耗资源)
  • 进程间不会相互影响,一个线程挂掉将导致整个进程挂掉(一列火车不会影响到另外一列火车,但是如果一列火车上中间的一节车厢着火了,将影响到所有车厢)
  • 进程可以拓展到多机,进程最多适合多核(不同火车可以开在多个轨道上,同一火车的车厢不能在行进的不同的轨道上)
  • 进程使用的内存地址可以上锁,即一个线程使用某些共享内存时,其他线程必须等它结束,才能使用这一块内存。(比如火车上的洗手间)-"互斥锁"
  • 进程使用的内存地址可以限定使用量(比如火车上的餐厅,最多只允许多少人进入,如果满了需要在门口等,等有人出来了才能进去)-“信号量”

Python进程线程

Multiprocessing 库

multiprocessing 是一个用与 threading 模块相似API的支持产生进程的包。multiprocessing 包同时提供本地和远程并发,使用子进程代替线程,有效避免 Global Interpreter Lock 带来的影响。

from multiprocessing import Pool

def f(x):
    return x*x

if __name__ == '__main__':
    with Pool(5) as p:
        print(p.map(f, [1, 2, 3]))

>>> [1,4,9]  #输出
  • Process
class multiprocessing.Process(group=None, target=None, name=None, args=(), kwargs={}, *, daemon=None)

multiprocessing中,通过创建一个 Process 对象然后调用它的 start() 方法来生成进程。 Processthreading.Thread API 相同。 一个简单的多进程程序示例是:

from multiprocessing import Process

def f(name):
    print('hello', name)

if __name__ == '__main__':
    p = Process(target=f, args=('bob',))
    p.start()
    p.join()

要显示所涉及的各个进程ID,这是一个扩展示例:

from multiprocessing import Process
import os

def info(title):
    print(title)
    print('module name:', __name__)
    print('parent process:', os.getppid())
    print('process id:', os.getpid())

def f(name):
    info('function f')
    print('hello', name)

if __name__ == '__main__':
    info('main line')
    p = Process(target=f, args=('bob',))
    p.start()
    p.join()

# 输出
main line
module name: __main__
parent process: 14993
process id: 26271
function f
module name: __main__
parent process: 26271
process id: 26762
hello bob
进程间通信

Process之间肯定是需要通信的,操作系统提供了很多机制来实现进程间的通信。Python 的 multiprocessing 模块包装了底层的机制,提供了QueuePipes等多种方式来交换数据。

Threading 库

Python3 线程中常用的两个模块为:

  • _thread
  • threading(推荐使用)
threading.Thread(group=None, target=None, name=None, args=(), kwargs={})

下面是threading.Thread提供的线程对象方法和属性:

  • start():创建线程后通过start启动线程,等待CPU调度,为run函数执行做准备;
  • run():线程开始执行的入口函数,函数体中会调用用户编写的target函数,或者执行被重载的run函数;
  • join([timeout]):阻塞挂起调用该函数的线程,直到被调用线程执行完成或超时。通常会在主线程中调用该方法,等待其他线程执行完成。
  • name、getName()&setName():线程名称相关的操作;
  • ident:整数类型的线程标识符,线程开始执行前(调用start之前)为None;
  • isAlive()、is_alive():start函数执行之后到run函数执行完之前都为True;
  • daemon、isDaemon()&setDaemon():守护线程相关;