多进程
进程是程序执行时的一个实例,从内核的观点看,进程的目的就是担当分配系统资源(CPU时间、内存等)的基本单位。它具有以下几个特点:
- 进程有独立的地址空间或内存空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响;
- 进程具有独立的数据空间,要进行数据的传递只能通过通信的方式进行(信号量、消息队列、共享内存、管道、文件、套接字);
- 对于服务器应用而言稳定性是最重要的,而多进程会比较容易实现这一点。一方面,当单个服务开启多个进程时,可以将前端的业务请求分流到不同的进程,提高业务并发数量;另一方面,在主进程里开启一个附属进程(如python里的os.system()方法),当主进程崩溃时,附属进程同样可以继续提供服务;
多线程
线程是进程的一个执行流,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。同一进程间的线程可共享的资源:
- 堆。堆是在进程空间中开辟出来的,所以它是理所当然地被共享的;
- 全局变量。它是与具体某一函数无关的,所以也与特定线程无关,因此也是共享的;
- 静态变量。对于线程内局部静态变量来说,其存放位置和全局变量一样,存于堆中开辟的.bss和.data段,是共享的;
- 文件等公用资源。使用这些公共资源的线程必须同步,几种同步资源的方式,包括信号、临界区、事件和互斥。
- 栈。栈是独享的,如局部变量。线程函数可以调用函数,而被调用函数中又是可以层层嵌套的,所以线程必须拥有自己的函数堆栈, 使得函数调用可以正常执行,不受其他线程的影响。
- 寄存器。电脑的寄存器是物理的,但线程里存放的是值的副本,如程序计数器PC。
多线程特点:
- 对于单核cpu而言,多线程并不是真正的并发,在操作系统层面还是串行的,只是线程间切换非常快速看起来是并行的。使用多线程的目的更多是避免阻塞,如游戏启动加载页面或做IO操作时,如果单线程处理,会造成非常卡顿的视觉效果;通过开启另个线程来做UI显示,可以来显示加载的进度,这样避免阻塞;
- 对于多核cpu而言,多线程可以实现真正的并发,充分发挥多核cpu的处理效率。
- 线程没有独立的数据空间,单个线程崩溃后,整个进程都会崩溃。
- 使多核cpu更加有效,操作系统会保证当线程数不大于cpu数目时,不同的线程运行于不同的cpu上。
- 改善程序结构。一个既长又复杂的进程可以考虑分为多个线程,成为几个独立或半独立的运行部分,这样的程序会利于理解和修改。
多进程 vs 多线程
- 单个进程内多线程,它们彼此之间使用相同的地址空间,共享大部分数据,启动一个线程所花费的空间远远小于启动一个进程所花费的空间;
- 线程间彼此切换所需的时间也远远小于进程间切换所需要的时间;
- 一个进程的开销大约是一个线程开销的30倍左右;
- 进程间数据传递只能通过通信的方式,耗时而且不方便;线程间可以通过全局变量来做数据传递;
- 对于进程,编程、调试简单,可靠性较高,但创建、销毁、切换速度慢,内存、资源占用大;对于线程,创建、销毁、切换速度快,内存、资源占用小,但编程、调试复杂,可靠性较差。
- 任务量较大时,多进程比多线程效率高;而完成的任务量较小时,多线程比多进程要快;
互斥锁
对于多线程而言,数据的共享也带来其他一些问题,有的变量不能同时被两个线程所修改,或者数据同时被两个线程读写,有的子程序中声明为static的数据更有可能给多线程程序带来灾难性的打击,这些正是编写多线程程序时最需要注意的地方,最常用的就是做互斥。
python多线程
对于一个全局变量,你的函数里如果只使用到了它的值,而没有对其赋值(指a= XXX这种写法)的话,就不需要声明global。
相反,如果你对其赋了值的话,那么你就需要声明global,表示你是在向一个全局变量赋值,而不是在向一个局部变量赋值。
若多个线程同时操作这一变量可能会导致抢占资源的现象,变量不能按照预定的逻辑进行操作,这时,在改变变量前需要对变量加互斥锁,操作完成后释放互斥锁。
对于python而言,其存在GIL(Global Interpreter Lock) 全局解释器锁的概念,导致任一时刻只能有一个线程使用python解释器,当用于 IO 密集型任务时,IO 期间线程会释放解释器。
在 CPU 计算繁忙的任务重 不建议使用多线程,在非 CPU繁忙型任务中建议使用多线程。
对于Python中使用多进程的好处:完全并行,无 GIL 的限制,可充分利用多 CPU 多核的环境。
|
|
对于上述代码,主线程里面开启个子线程,如果不加锁,输出乱码,逻辑混乱:hh e l e l l ol w o o r w l
如果加入互斥锁,输出正常:h e l l o w o r l d h e l l o