GIL锁

什么是GIL

GIL全局解释器锁,是防止多个线程在同一时间同时执行的、CPython解释器特有的一种互斥锁。 每一个py文件都会有自己的解释器,也就是说不同py文件的GIL都是独立的, ps:分散于不同进程内的线程不会去争抢同一把GIL,只有同一个进程的多个线程才争抢同一把GIL。

为什么需要GIL

CPython解释器的内存管理机制是非线程安全的,所以通过GIL使程序串行,可以保证数据安全,但会降低执行效率。 非线程安全属于历史遗留问题,而一旦修改,原来基于GIL的程序都要修改。

GIL带来的问题

即便多核处理器,也无法真正并行(可以并发),进程内线程只能并发,同一时刻只有一个线程可以运行

GIL的加锁和解锁时机(待补充)

加锁的时机:在调用解释器时立即加锁

解锁时机:

  • 当前线程遇到了IO时释放

  • 当前线程执行时间超过设定值时释放,解释器会检测线程的执行时间,一旦到达某个阈值,通知线程保存状态切换线程,以此来保证数据安全

多进程与多线程的使用情况分析:

1、单核时,无论是IO密集还是计算密集,GIL都没有影响; 2、多核下,IO密集型会受到GIL的影响,但是影响不大,此时优先考虑多线程处理数据; 3、多核下,计算密集型受到GIL影响较大,此时使用多进程可以提高效率,因为CPython多线程无法并行,但多进程可以。

程序运行时间对比:

# 纯计算,大量,进程更快(如果只是少量计算,线程未必慢,因为开启进程比开启线程更费时间)
from threading import  Thread
from multiprocessing import  Process
import time


a = 1
def task():
   global a
   for i in range(10000000):
       a +=1
       a * 10 / 2 - 3


# 多线程
s = time.time()
t1 = Thread(target=task)
t2 = Thread(target=task)
t3 = Thread(target=task)
t1.start()
t2.start()
t3.start()

t1.join()
t2.join()
t3.join()
print(time.time()-s)

# 多进程
if __name__ == '__main__':
   s = time.time()
   t1 = Process(target=task)
   t2 = Process(target=task)
   t3 = Process(target=task)
   t1.start()
   t2.start()
   t3.start()

   t1.join()
   t2.join()
   t3.join()

   print(time.time()-s)
GIL与自定义互斥锁的异同

GIL作用于全局,自定义互斥锁作用于局部 线程必须先获取GIL才能去拿自定义互斥锁 单进程内的所有线程都会去抢GIL 单进程内的只有一部分线程会去抢自定义的互斥锁

进程池与线程池

是一种容器,本质是储存进程或线程的列表 IO密集使用线程池,计算密集使用进程池 线程/进程池不仅帮我们控制线程/进程的数量,还帮我们完成了线程/进程的创建,销毁,以及任务的分配

池与信号量的区别:

信号量的作用是同一时间某一个数据能被指定数量的进程或线程访问 而池控制的是数量,没有对数据访问进行限制

进程池:

from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor
import time,os

# 创建进程池,指定最大进程数为3,此时不会创建进程,不指定数量时,默认为CPU核数
pool = ProcessPoolExecutor(3)

def task():
   time.sleep(1)
   print(os.getpid(),"working..")

if __name__ == '__main__':
   for i in range(10):
       pool.submit(task) # 提交任务时立即创建进程

   # 任务执行完成后也不会立即销毁进程
   time.sleep(2)

   for i in range(10):
       pool.submit(task) #再有新任务时 直接使用之前已经创建好的进程来执行

输出结果:(每次可能都不同)
13760 working..
26536 working..
21388 working..
13760 working..
26536 working..
21388 working..
26536 working..
13760 working..
21388 working..
26536 working..
13760 working..
21388 working..
13760 working..
26536 working..
21388 working..
13760 working..
26536 working..
21388 working..
13760 working..
26536 working..

线程池:

from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor
from threading import current_thread,active_count
import time,os

# 创建进程池,指定最大线程数为3,此时不会创建线程,不指定数量时,默认为CPU和核数*5
pool = ThreadPoolExecutor(3)
print(active_count()) # 只有一个主线

def task():
   time.sleep(1)
   print(current_thread().name,"working..")


for i in range(10):
   pool.submit(task) # 第一次提交任务时立即创建线程
# 任务执行完成后也不会立即销毁
time.sleep(2)

for i in range(10):
   pool.submit(task) #再有新任务是 直接使用之前已经创建好的线程来执行

阻塞vs非阻塞

阻塞、就绪、运行指的是进程的三种执行状态。 阻塞=》遇到io操作,无法继续执行,并且立刻释放CPU资源 非阻塞=》正常执行中(就绪、运行),没有遇到IO操作,或者通过某种手段让程序即便遇到IO也不会停在原地,而是执行其他操作。

总结:阻塞与非阻塞指的是程序的执行状态

同步与异步

同步与异步:指的是提交任务的两种方式

同步(调用/执行/任务/提交):提交完任务后就在原地等待,拿到返回值后才能继续执行下一行代码(可能是IO,也可能是因为计算时间较长等原因)。 异步(调用/执行/任务/提交):提交完任务后不在原地等待,不关心任务的结果,直接执行下一行代码。

异步效率高于同步。

异步提交任务时,任务可能包含IO操作,异步也可能阻塞。 同步提交任务时,也可能会因为计算复杂而卡,不等同与阻塞。

程序中的异步调用并获取结果:

from concurrent.futures import ThreadPoolExecutor  # 导入线程池
from threading import current_thread  # 从线程中导入查看当前线程的方法
import time

pool = ThreadPoolExecutor(3)


def task(i):
   time.sleep(0.01)
   print(current_thread().name,"working..")
   return i ** i
objs = []
for i in range(1,5):
   res_obj = pool.submit(task,i) # 异步方式提交任务,会返回一个对象用于表示任务结果,即pool.submit()会生成对象
   objs.append(res_obj)

pool.shutdown(wait=True)  # shutdown是一个阻塞函数,指的是不能再往进程池内提交任务,wait=True等待进程池或线程池内所有的任务都运行完毕,才允许代码往下继续执行。

# 从结果对象中取出执行结果
for res_obj in objs:  # 从列表中拿出对象
   print(res_obj.result()) # 由submit得到的对象会有result()方法,即对象内封装的返回值
print("over")

程序中的同步调用并获取结果:(等到返回结果才能继续执行)

from  concurrent.futures import ThreadPoolExecutor
import time,random

def task(i):
   print("%s run"%i)
   time.sleep(random.randint(1,3))
   return i**2  # 返回值,这也是result方法的要求。
pool = ThreadPoolExecutor(3)
for i in range(6):
   p = pool.submit(task,i)
   res = p.result()
   print(res)
print("over")
输出结果:
0 run
0
1 run
1
2 run
4
3 run
9
4 run
16
5 run
25
over

20190103(GIL,池,阻塞,同步异步)的更多相关文章

  1. python GIL锁、进程池与线程池、同步异步

    一.GIL全局解释器锁 全局解释器锁 在CPython中,全局解释器锁(GIL)是一个互斥锁,它可以防止多个本机线程同时执行Python代码.之所以需要这个锁,主要是因为CPython的内存管理不是线 ...

  2. GIL锁、进程池与线程池、同步异步

    GIL锁定义 GIL锁:Global Interpreter Lock  全局解释器 本质上是一把互斥锁 官方解释: 在CPython中,这个全局解释器锁,也称为GIL,是一个互斥锁,防止多个线程在同 ...

  3. python 之 并发编程(进程池与线程池、同步异步阻塞非阻塞、线程queue)

    9.11 进程池与线程池 池子使用来限制并发的任务数目,限制我们的计算机在一个自己可承受的范围内去并发地执行任务 池子内什么时候装进程:并发的任务属于计算密集型 池子内什么时候装线程:并发的任务属于I ...

  4. 进程(并发,并行) join start 进程池 (同步异步)

    一.背景知识 顾名思义,进程即正在执行的一个过程.进程是对正在运行程序的一个抽象.进程的概念起源于操作系统,是操作系统最核心的概念,也是操作系统提供的最古老也是最重要的抽象概念之一.操作系统的其他所有 ...

  5. 进程理论 阻塞非阻塞 同步异步 I/O操作

    1.什么是进程 进程指的是一个正在运行的程序,进程是用来描述程序执行过程的虚拟概念 进程的概念起源于操作系统,进程是操作系统最核心的概念,操作系统其它所有的概念都是围绕进程来的 2.操作系统 操作系统 ...

  6. 阻塞 , 非阻塞 , 同步 ,异步 , I/O模型

    •阻塞,非阻塞:进程/线程要访问的数据是否就绪,进程/线程是否需要等待: •同步,异步:访问数据的方式,同步需要主动读写数据,在读写数据的过程中还是会阻塞:异步只需要I/O操作完成的通知,并不主动读写 ...

  7. nio 阻塞 非阻塞 同步 异步

    https://mp.weixin.qq.com/s/5SKgdkC0kaHN495psLd3Tg 说在前面 上篇NIO相关基础篇二,主要介绍了文件锁.以及比较关键的Selector,本篇继续NIO相 ...

  8. python 管道 事件(Event) 信号量 进程池(map/同步/异步)回调函数

    ####################总结######################## 管道:是进程间通信的第二种方式,但是不推荐使用,因为管道会导致数据不安全的情况出现 事件:当我运行主进程的 ...

  9. GIL全局解释器锁,线程池与进程池 同步异步,阻塞与非阻塞,异步回调

    GIL全局解释器锁 1.什么是GIL 官方解释:'''In CPython, the global interpreter lock, or GIL, is a mutex that prevents ...

  10. GIL 线程池 进程池 同步 异步

    1.GIL(理论 重点)2.线程池 进程池3.同步 异步 GIL 是一个全局解释器锁,是一个互斥锁 为了防止竞争解释器资源而产生的 为何需要gil:因为一个python.exe进程中只有一份解释器,如 ...

随机推荐

  1. C++中的引用和指针

    引用和指针有何区别?何时只能使用指针而不能使用引用?    引用是一个别名,不能为 NULL 值,不能被重新分配:指针是一个存放地址的变量.当需要对变量重新赋以另外的地址或赋值为 NULL 时只能使用 ...

  2. css清除浮动好方法

    1.clear:both ==>IE6,7会有高度,所以去高度需要.clear{ clear:both; height:0px; margin:0; padding:0; width:0; bo ...

  3. 微信小程序:点击预览图片

    在开发微信小程序时,开发人员会参考着小程序api来开发小程序,但有的时候根据情况不同很容易出现bug,以下是我在开发小程序时出现的各种bug,在开发时有需要预览图片. 1.xml <view c ...

  4. uvm_tlm_if_base——TLM1事务级建模方法(三)

    文件: src/tlm1/uvm_tlm_ifs.svh 类: uvm_tlm_if_base 这个类没有派生自任何类,在类的中,定义了三类接口:第一类是阻塞性质的普通方法(task),put, ge ...

  5. javascript的Array.prototype.map()和jQuery的jQuery.map()

    两个方法都可以根据现有数组创建新数组,但在使用过程中发现有些不同之处 以下面这个数据为例: var numbers = [1, 3, 4, 6, 9]; 1. 对undefined和null的处理 a ...

  6. C#之linq

    本文根据30分钟LINQ教程学习作的笔记. 1.Guid.Empty Guid 结构: 表示全局唯一标识符 (GUID).Empty字段:Guid 结构的只读实例,其值均为零.用来设置初始值.   G ...

  7. Oracle开发›如何取出每个分组的第一条记

    <ignore_js_op> 截屏图片 (2).jpg (43.34 KB, 下载次数: 21) 下载附件 2012-11-7 12:36 上传   如何取出每个分组的第一条记录(黄色背景 ...

  8. 2018.2.2 JavaScript中的封装

    JavaScript中的封装 1.封装的概念 通过将一个方法或者属性声明为私用的,可以让对象的实现细节对其他对象保密以降低对象之间的耦合程度,可以保持数据的完整性并对其修改方式加以约束,这样可以使代码 ...

  9. C语言 流缓冲 Stream Buffering

    From : https://www.gnu.org/software/libc/manual/html_node/Stream-Buffering.html 译者:李秋豪 12.20 流缓冲 通常情 ...

  10. python剑指offer 实现树的子结构

    题目描述 输入两棵二叉树A,B,判断B是不是A的子结构.(ps:我们约定空树不是任意一个树的子结构) # -*- coding:utf-8 -*- # class TreeNode: # def __ ...