Python多进程和多线程是鸡肋嘛?【转】

GIL是什么

Python的代码执行由 Python虚拟机(也叫解释器主循环,CPython版本)来控制,Python在设计之初就考虑到在解释器的主循环中,同时只有一个线程在运行。即每个CPU在任意时刻只有一个线程在解释器中运行。对 Python虚拟机访问的控制由全局解释锁GIL控制,正是这个锁来控制同一时刻只有一个线程能够运行。——在单核CPU下的多线程其实都只是并发,不是并行 。

并发与并行区别

  • 并发:两个或多个事件在同一时间间隔发生,或者说交替做不同事件的能力,或者说不同的代码块交替执行。
  • 并行:两个或者多个事件在同一时刻发生,或者说同时做不同事件的能力,或者说不同的代码块同时执行。

并发和并行的意义

并发和并行都可以处理“多任务”,二者的主要区别在于是否是“同时进行”多个的任务。但是涉及到任务分解(有先后依赖耦合度高的任务无法做到并行)、任务运行(可能要考虑互斥、锁、共享等)、结果合并。


Python 下的多线程

在Python多线程下,每个线程的执行方式,如下:

  1. 获取GIL
  2. 切换到这个线程去执行
  3. 运行代码,这里有两种机制:
  4. 指定数量的字节码指令(100个)
  5. 固定时间15ms线程主动让出控制
  6. 把线程设置为睡眠状态
  7. 释放GIL
  8. 再次重复以上步骤

在Python2中,在解释器解释执行任何 Python 代码时,都需要先获得这把锁才行(同一时间只会有一个获得了 GIL 的线程在跑,其它的线程都处于等待状态等着 GIL 的释放),在遇到 I/O 操作时会释放这把锁。如果是纯计算的程序,没有 I/O 操作,解释器会每隔 100 次操作就释放这把锁,让别的线程有机会执行(这个次数可以通过 sys.setcheckinterval 来调整)也正是这种设定,是的多线程的CPU密集型计算非常鸡肋,下面会讲到为何如此。

而在python3中,GIL不使用ticks计数(100次,释放GIL),改为使用计时器(执行时间达到15ms阈值后,当前线程释放 GIL),使得执行计算的次数更多,释放次数减少,这样对CPU密集型程序更加友好,但依然没有解决GIL导致的同一时间只能执行一个线程的问题,所以效率依然不尽如人意。


那么是不是Python的多线程是鸡肋嘛?

CPU密集型(各种循环处理、计数等等),在这种情况下,ticks计数很快就会达到阈值,然后触发GIL的释放与再竞争(多个线程来回切换是需要消耗资源的),所以python下的多线程对 CPU密集型代码并不友好,会触发相当频繁的线程切换。

IO密集型(文件处理、网络爬虫等),多线程能够有效提升效率(单线程下有IO操作会进行IO 等待,造成不必要的时间浪费,而开启多线程能在线程A等待时,自动切换到线程B,可以不浪费 CPU的资源,从而能提升程序执行效率,一个线程获得GIL发送消息,然后等待返回消息(阻塞),Python此时释放GIL, 其他线程得到GIL发送消息,然后同样等待返回消息(阻塞)......,这样保证了IO传输过程时间的合理利用,减少了IO等待造成的资源浪费,提高IO传输效率)。所以python的多线程对IO密集型代码比较友好。


有哪些结论?

I/O密集型使用多线程并发执行提高效率、计算密集型使用多进程(multiprocessing )并行执行提高效率。通常程序既包含IO操作又包含计算操作,那么这种情况下,在开始并发任务之前,可以先进行测试,测试多线程、多进程哪个效率高就是用哪种方式。

请注意:多核多线程比单核多线程更差,多核多进程下,CPU1释放GIL后,其他CPU上的线程都会进行竞争,但GIL可能会马上又被CPU1拿到,CPU2释放GIL后……,导致其他几个CPU上被唤醒后的线程会醒着等待到切换时间后又进入待调度状态,这样会造成线程颠簸(thrashing),导致效率更低。

多线程下的CPU密集型计算也不是无药可医,可以利用ctypes绕过GIL,ctypes可以使py直接调用任意的C动态库的导出函数。所要做的只是把关键部分用 C/C++ 写成 Python 扩展。而且,ctypes会在调用C函数前释放GIL。

同时,可以了解下协程,又称微线程。

协程最大的优势就是协程极高的执行效率。因为子程序切换不是线程切换,而是由程序自身控制,因此,没有线程切换的开销,和多线程比,线程数量越多,协程的性能优势就越明显。

第二大优势就是不需要多线程的锁机制,因为只有一个线程,也不存在同时写变量冲突,在协程中控制共享资源不加锁,只需要判断状态就好了,所以执行效率比多线程高很多。

因为协程是一个线程执行,那怎么利用多核CPU呢?最简单的方法是多进程+协程,既充分利用多核,又充分发挥协程的高效率,可获得极高的性能。

转自

Python多进程和多线程是鸡肋嘛? https://www.toutiao.com/a6738231652184490247/

原文地址:https://www.cnblogs.com/paul8339/p/11557937.html

时间: 2024-07-31 06:58:32

Python多进程和多线程是鸡肋嘛?【转】的相关文章

(转)python多进程、多线程编程

1 概念梳理: 1.1 线程 1.1.1 什么是线程 线程是操作系统能够进行运算调度的最小单位.它被包含在进程之中,是进程中的实际运作单位.一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务.一个线程是一个execution context(执行上下文),即一个cpu执行时所需要的一串指令. 1.1.2 线程的工作方式 假设你正在读一本书,没有读完,你想休息一下,但是你想在回来时恢复到当时读的具体进度.有一个方法就是记下页数.行数与字数这三个数值,这

第十五章 Python多进程与多线程

15.1 multiprocessing multiprocessing是多进程模块,多进程提供了任务并发性,能充分利用多核处理器.避免了GIL(全局解释锁)对资源的影响. 有以下常用类: 类 描述 Process(group=None, target=None, name=None, args=(), kwargs={}) 派生一个进程对象,然后调用start()方法启动 Pool(processes=None, initializer=None, initargs=()) 返回一个进程池对象

Python 多进程、多线程效率比较

Python 界有条不成文的准则: 计算密集型任务适合多进程,IO 密集型任务适合多线程.本篇来作个比较. 通常来说多线程相对于多进程有优势,因为创建一个进程开销比较大,然而因为在 python 中有 GIL 这把大锁的存在,导致执行计算密集型任务时多线程实际只能是单线程.而且由于线程之间切换的开销导致多线程往往比实际的单线程还要慢,所以在 python 中计算密集型任务通常使用多进程,因为各个进程有各自独立的 GIL,互不干扰. 而在 IO 密集型任务中,CPU 时常处于等待状态,操作系统需要

Python多进程与多线程

1.基本概念 2.多线程内容方法 3.多进程内容方法 1.基本概念 1.1 线程 1.1.1 什么是线程 线程是操作系统能够进行运算调度的最小单位.它被包含在进程之中,是进程中的实际运作单位.一条线程指的是进程中一个单一顺序的控制流, 一个进程中可以并发多个线程,每条线程并行执行不同的任务.A thread is an execution context, which is all the information a CPU needs to execute a stream of instru

python多进程与多线程使用场景

多进程与多线程区别 多进程使用场景 多线程使用场景

python 多进程与多线程浅析

python多线程是伪多线程,同时间最多只有一个线程在执行,但这样并不代码python的多线程没有作用,对于IO密集型的系统,python的多线程还是能极大的提升性能- 关于python伪多线程可以去了解python GIL的概念. 以下代码涉及python多线程,多进程,进程池相关操作: #encoding:utf-8 from multiprocessing import Pool,Manager,cpu_count,Lock,Process import thread import thr

Python多进程和多线程

多进程 Unix/Linux操作系统提供了一个fork()系统调用,它非常特殊.普通的函数调用,调用一次,返回一次,但是fork()调用一次,返回两次,因为操作系统自动把当前进程(称为父进程)复制了一份(称为子进程),然后,分别在父进程和子进程内返回. 想要在windows平台编写多进程,需要引入multiprocessing模块. from multiprocessing import Process import os def run_proc(name): print('child pid

python多进程和多线程---(自学中,坚持更新)

首先我们知道进程和线程最明显的区别是: 多进程环境中,每个进程有自己的进程数据,各个进程数据之间是相互独立的.一个进程可以有多个线程,多个线程共享该进程的数据. #-*- coding:utf-8 -*- import os from nt import getpid print("current id: %d" %getpid()) 输出 current id: 17452 创建进程: 创建进程,linux下用fork,和linux环境下C编程的fork函数一样,对于父进程而言,返回

python 多进程、多线程

1.多线程: 下面讲一个简单用法,这个模块比较简单,但是实际使用中会遇到很多坑 from multiprocessing import process def go(s): print "主线程 %s " % s if __name__ == "__main__": p = process.Process(target=go, args=(2,)) p.start() 2.多线程: from threading import Thread def go(s): pr