进程/线程/协程/GIL
Posted abysschen
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了进程/线程/协程/GIL相关的知识,希望对你有一定的参考价值。
线程
线程是操作系统调度的最小单位
threading模块
线程的调用方式:
import threading import time ‘‘‘直接调用‘‘‘ def hello(name): print("Hello %s"%name) time.sleep(3) if __name__ == "__main__": t1=threading.Thread(target=hello,args=("zhangsan",)) #生成线程实例 t2=threading.Thread(target=hello,args=("lisi",)) t1.setName("aaa") #设置线程名 t1.start() #启动线程 t2.start() t2.join() #join 等待t2先执行完 print("Hello") print(t1.getName()) #获取线程名
setDaemon线程
#!/usr/bin/env python # -*- coding:utf-8 -*- import time import threading def run(n): print(‘Hello..[%s] ‘ % n) time.sleep(2) def main(): for i in range(5): t = threading.Thread(target=run,args=[i,]) t.start() t.join(1) m = threading.Thread(target=main,args=[]) m.setDaemon(True) #将主线程设置Daemon设置为True后,主线程执行完成时,其它子线程会同时退出,不管是否执行完任务 m.start() print("--- done----")
线程锁Lock
import threading import time num = 100 #设置一个共享变量 lock=threading.Lock() #生成全局锁 def show(): global num #在函数内操作函数外变量,需设置为全局变量 time.sleep(1) lock.acquire() #修改前加锁 num -= 1 lock.release() #修改后解锁 list=[] for i in range(100): t = threading.Thread(target=show) t.start() list.append(t) for t in list: t.join() print(num)
进程
multiprocessing模块
from multiprocessing import Process import time def start(name): time.sleep(1) print(‘hello‘, name) if __name__ == "__main__": p = Process(target=start, args=(‘zhangsan‘,)) p1 = Process(target=start, args=(‘lisi‘,)) p.start() p1.start() p.join()
进程间通讯
每个进程都拥有自己的内存空间,因此不同进程间内存是不共享的,要想实现两个进程间的数据交换,有几种方法
Queue(队列)
from multiprocessing import Process, Queue def start(q): q.put(‘hello‘) if __name__ == "__main__": q = Queue() p = Process(target=start, args=(q,)) p.start() print(q.get()) p.join()
协程
协程,又称微线程,是一种用户态的轻量级线程。协程能保留上一次调用时的状态,每次过程重入时,就相当于进入上一次调用的状态,换种说法:进入上一次离开时所处逻辑流的位置,当程序中存在大量不需要CPU的操作时(IO),适用于协程。
协程有极高的执行效率,因为子程序切换不是线程切换,而是由程序自身控制,因此,没有线程切换的开销。
不需要多线程的锁机制,因为只有一个线程,也不存在同时写变量冲突,在协程中控制共享资源不加锁,只需要判断状态就好了,所以执行效率比多线程高很多。
因为协程是一个线程执行,所以想要利用多核CPU,最简单的方法是多进程+协程,这样既充分利用多核,又充分发挥协程的高效率。
那符合什么条件就能称之为协程:1、必须在只有一个单线程里实现并发 2、修改共享数据不需加锁 3、用户程序里自己保存多个控制流的上下文栈 4、一个协程遇到IO操作自动切换到其它协程
python中对于协程有两个模块,greenlet和gevent。
Greenlet
# Greenlet(greenlet的执行顺序需要我们手动控制) from greenlet import greenlet def test1(): print (11) gr2.switch() #手动切换 print (22) gr2.switch() def test2(): print (33) gr1.switch() print (44) gr1 = greenlet(test1) gr2 = greenlet(test2) gr1.switch()
gevent(自动切换,由于切换是在IO操作时自动完成,所以gevent需要修改Python自带的一些标准库,这一过程在启动时通过monkey patch完成)
from gevent import monkey; monkey.patch_all() import gevent import time def foo(): print(‘11‘) time.sleep(3) print(‘22‘) def bar(): print(‘33‘) print(‘44‘) gevent.joinall([ gevent.spawn(foo), gevent.spawn(bar), ])
GIL锁
1.什么是GIL?
GIL-全局解释器锁
GIL只在CPython解释器上存在,在Python的解释器中,使用的最多的是CPython解释器,所以不可避免的会遇到GIL
2.GIL对程序的影响
Pyhton的多线程是伪多线程,是并发的,因为无论如何都逃不过GIL解释器锁
因为GIL的存在,Python中同一时刻有且只有一个线程会执行,所以Python多线程无法利用多核CPU,所以Python的多线程不适合计算密集型的程序,只适合IO密集型
3.为什么多线程适合IO密集型程序?
通常程序分为两种,1.计算密集型程序 2.IO密集型程序
大部分的程序在运行时,都需要大量的IO操作,如网络数据的收发,大文件的读写
IO密集型程序在运行时,需要大量的时间进行等待,如果IO操作不完成,程序无法执行后面的操作,一直处于等待状态,导致CPU空闲.Python解释器在程序执行IO等待时,会释放GIL锁,让其他线程执行,提高Python程序的执行效率.
4.GIL的优缺点
优点:
避免了大量的加锁解锁
使数据更加安全,解决多线程间的数据完整性和状态同步
缺点:
多核处理器退化成单核处理器,只能并发不能并行
5.如何改善GIL产生的问题
1.用多进程代替多线程
2.更换Python的解释器,如JPython,但其支持的模块少,用的人也少
6.有了GIl锁为什么还需要互斥锁
因为多个线程之间的GIL锁是自动获取和释放的,我们无法提前获知,所以需要自己加锁.
以上是关于进程/线程/协程/GIL的主要内容,如果未能解决你的问题,请参考以下文章