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. java使用线程请求访问每次间隔10分钟连续5次,之后停止请求

    java使用线程请求访问每次间隔10分钟连续5次,收到相应的时候停止请求 package com.qlwb.business.util; /** * * * @类编号: * @类名称:RequestT ...

  2. 操作文件方法简单总结(File,Directory,StreamReader,StreamWrite )(转载)

    本文转自http://www.cnblogs.com/zery/p/3315889.html 对于文件夹,文档的操作一直处于一知半解状态,有时间闲下来了,好好练习了一把,对文档,文件的操作有了一个基本 ...

  3. 个人整理的jsp、EL表达式、JSTL标签库的笔记,少概念多实用,需要的留下邮箱,会第一时间分享原稿PDF给大家!

    jsp 第一章 jsp介绍及对比servlet 作用: 动态网页技术,动态的从数据库获取数据 jsp和servlet的优缺点: jsp优点:页面表现方便,利于写html代码 jsp缺点:业务逻辑处理麻 ...

  4. JQuery notepad

    ready:在文档加载后执行,在文档对象加载完毕后,页面完全显示后执行,把所有事件函数放在ready中加载是一种非常好的方法,ready() 函数不应与 <body onload="& ...

  5. Counting blessings can actually increase happiness and health by reminding us of the good things in life.

    Counting blessings can actually increase happiness and health by reminding us of the good things in ...

  6. Android学习笔记1——Android开发环境配置

    一.JDK配置 Android是基于Java进行开发的,首先需要在电脑上配置JDK(Java Development Kit).在http://www.androiddevtools.cn/下载对应系 ...

  7. vue组件总结(三)

    一.什么是组件 组件(component)是Vue最强大的功能之一.组件可以扩展HTML元素,封装可重用的代码,根据项目需求,抽象出一些组件,每个组件里包含了展现.功能和样式.每个页面,根据自己的需要 ...

  8. MVC下c#对接微信公众平台开发者模式

    在ashx文件中进行HttpContext的处理: using System; using System.Collections.Generic; using System.Linq; using S ...

  9. 详解Unity 4.6新UI的布局

    本文所讲的是Unity 4.6中新加入的uGUI,官方称Unity UI,而不是过去的OnGUI式的旧UI(官方称Legacy GUI). 我曾经在8月份对照4.6 Beta的文档写过一篇笔记学习Un ...

  10. eclipse Indigo搭建SSH框架详解

    SSH框架是最常用的框架之一,在搭建SSH框架的时候总有人遇到这样,那样的问题.下面我介绍一下SSH框架搭建的全过程. 第一步:准备工作.   下载好eclipse,Struts2,Spring,Hi ...