GIL锁

Posted lanlan999

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了GIL锁相关的知识,希望对你有一定的参考价值。

一、概念理论

1、什么是GIL

全局解释器锁 ,是加在解释器上的互斥锁。

?为什么需要GIL

由于CPython的内存管理是非线程安全,于是CPython就给解释器加了个锁,解决了安全问题

但是降低了效率

另外 虽然有解决方案 但是由于牵涉太多,一旦修改则 很多以前的基于GIL的程序都需要修改,

所以变成了历史遗留问题

2、 GIL带来的问题

即使在多核处理器下, 也无法真正的并行。

总结:

1.在单核情况下,无论是IO密集还是计算密集 GIL都不会产生影响

2.如果是多核下,IO密集型会受到GIL的影响,但是很明显IO速度远比计算速度慢

3.IO密集型用多线程,因为多线程开销小,节省资源, 对于计算密集型 应该使用多进程,因为在

CPython多线程是无法并行的

二、多进程与多线程效率对比

from threading import Thread
from multiprocessing import Process
import time
a=1
def task():
   global a
   for i in range(10000):
       a+=1
       a*10/2-3
s=time.time()
?
?
if __name__ == ‘__main__‘:
   # 多线程
   # t1 = Thread(target=task)
   # t2 = Thread(target=task)
   # t3 = Thread(target=task)
   # t1.start()
   # t2.start()
   # t3.start()
?
   p1=Process(target=task)
   p2=Process(target=task)
   p3=Process(target=task)
   p1.start()
   p2.start()
   p3.start()
?
   print(time.time()-s)

三、GIL与自定义线程锁的区别

from threading import  Thread,Lock
import time
?
lock = Lock()
a = 0
def task():
   global a
   lock.acquire()
   temp = a
   time.sleep(0.01)
   a = temp + 1
   lock.release()
?
ts = []
for i in range(10):
   t1 = Thread(target=task)
   t1.start()
   ts.append(t1)
?
for i in ts:
   i.join()
?
print(a)

GIL使用用于保护解释器相关的数据,解释器也是一段程序,肯定有其定义各种数据 GIL并不能保证你自己定义的数据的安全,所以一旦你的程序中出现了多线程共享数据时就需要自己加锁

四、线程池的使用

 

?
?
from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor
from  threading import active_count,current_thread
import os,time
# 创建线程池 指定最大线程数为3 如果不指定 默认为CPU核心数 * 5
# pool = ThreadPoolExecutor(3)# 不会立即开启子线程
#
# print(active_count())
#
# def task():
#     print("%s running.." % current_thread().name)
#     time.sleep(1)
#
# #提交任务到线程池
# for i in range(10):
#     pool.submit(task)
#
?
?
# 创建进程池 最大进程数为3 默认为cpu个数
pool = ProcessPoolExecutor(3)# 不会立即开启子进程
?
# time.sleep(10)
?
def task():
   print("%s running.." % os.getpid())
   time.sleep(1)
?
if __name__ == ‘__main__‘:
   # #提交任务到进程池
   for i in range(10):
       pool.submit(task) # 第一次提交任务时会创建进程 ,后续再提交任务,直接交给以及存在的进程来完成,如果没有空闲进程就等待
?
?
# 与信号量的区别 ,信号量也是一种锁 适用于保证同一时间能有多少个进程或线程访问
# 而线程/进程池,没有对数据访问进行限制仅仅是控制数量
?
?
?

五、同步与异步

"""
阻塞 非阻塞
程序遇到了IO操作,无法继续执行代码,叫做阻塞
程序没有遇到IO操作,正常执行中,就叫非阻塞
它们指的是程序的状态
?
  就绪 运行 阻塞
?
就绪和阻塞给人的感觉就是卡主了
?
?
同步 异步
同步(调用/执行/任务/提交),发起任务后必须等待任务结束,拿到一个结果才能继续运行
异步                     发起任务后不需要关系任务的执行过程,可以继续往下运行
?
异步效率高于同步
但是并不是所有任务都可以异步执行,判断一个任务是否可以异步的条件是,任务发起方是否立即需要执行结果
?
?
同步不等于阻塞 异步不等于非阻塞
当使用异步方式发起任务时 任务中可能包含io操作 异步也可能阻塞
同步提交任务 也会卡主程序 但是不等同阻塞,因为任务中可能在做一对计算任务,CPU没走
"""
?
# 使用线程池 来执行异步任务
?
from concurrent.futures import ThreadPoolExecutor
import time
pool = ThreadPoolExecutor()
?
?
def task(i):
?
   time.sleep(1)
   print("sub thread run..")
   i += 100
   return i
?
fs = []
for i in range(10):
   f = pool.submit(task,i) # submit就是一异步的方式提交任务
   # print(f)
   # print(f.result()) # result是阻塞的 会等到这任务执行完成才继续执行 ,会异步变成同步
   fs.append(f)
?
?
?
# 是一个阻塞函数,会等到池子中所有任务完成后继续执行
pool.shutdown(wait=True)
?
# pool.submit(task,1) # 注意 在shutdown之后 就不能提交新任务了
?
for i in fs:
   print(i.result())
?
print("over")

 

 



























































































































以上是关于GIL锁的主要内容,如果未能解决你的问题,请参考以下文章

GIL全局解释器锁

GIL全局解释器锁及协程

Python入门学习-DAY36-GIL全局解释器锁死锁现象与递归锁信号量Event事件线程queue

Python11/12--GIL/互斥锁/进程池

python GIL锁进程池与线程池同步异步

python GIL锁