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. Sqoop Export HDFS

    Sqoop Export应用场景——直接导出 直接导出 我们先复制一个表,然后将上一篇博文(Sqoop Import HDFS)导入的数据再导出到我们所复制的表里. sqoop export \ -- ...

  2. Maven的学习资料收集--(七) 构建Spring项目

    在这里,使用Maven构建一个Spring项目 构建单独项目的话,其实都差不多 1. 新建一个Web项目 参考之前的博客 2.修改 pom.xml,添加Spring依赖 <project xml ...

  3. orcale开篇

    1.数据库系统和数据库的管理系统  数据库系统=数据库的管理系统+oper操作员+硬件2.Oracle的版本  8i/ 9i 10g/11g  12c(cloud)3.实例和数据库的关系  实例:数据 ...

  4. java 多线程之取消与关闭

    要使线程安全,快速,可靠的停下来并不是一件容易的事情.java并没有提供任何机制来安全的终止线程.但是java提供了中断(interrupt)使一个线程可以终止另一个线程的当前工作 每个线程都有一个b ...

  5. [荐]推荐一个shell学习的网站

    最近再用shell脚本,发现一个脚本学习的网站,非常好用,特此推荐一下. shell学习网站链接:http://c.biancheng.net/cpp/shell/

  6. cms-登陆

    先介绍下登陆的思路: 1.在登陆页面首先前端验证用户名和密码是否正确,如果验证通过,则ajax的方式向后台提交数据. 2.在controller层,将得到的用户名名和密码封装进shiro的token, ...

  7. NYOJ-596-谁是最好的Coder

    原题链接 谁是最好的Coder 时间限制:1000 ms  |  内存限制:65535 KB 难度:0 描述 计科班有很多Coder,帅帅想知道自己是不是综合实力最强的coder. 帅帅喜欢帅,所以他 ...

  8. git入门使用摘录

    无论使用github或者gitlab,第一步都是在本地生产ssh-key,ssh-key作为客户端的身份证存放在user用户的.ssh文件夹下.如果之前没有生产过,需要用ssh-keygen命令生成. ...

  9. Hbase各种查询总结

    运用hbase好长时间了,今天利用闲暇时间把Hbase的各种查询总结下,以后有时间把协处理器和自定义File总结下. 查询条件分为: 1.统计表数据 2,hbase 简单分页 3,like 查询 4  ...

  10. 地理位置索引 2d索引

    地址位置索引:将一些点的位置存储在mongodb中,创建索引后,可以按照位置来查找其他点 子分类: .2d索引:平面地理位置索引,用于存储和查找平面上的点. .2dsphere索引:球面地理位置索引, ...