一、GIL全局解释器锁 global interpreter lock

1、GIL是一个互斥锁:保证数据的安全(以牺牲效率来换取数据的安全),阻止同一个进程内多个线程同时执行(不能并行但是能够实现并发)

2、GIL全局解释器存在的原因是因为Cpython解释器的内存管理不是线程安全的

3、CIL是一个互斥锁,是加在Cpython解释器上的,同一进程内的所有线程都需要先抢到GIL锁,才能执行解释器代码

4、GIL优缺点:

    优点:保证Cpython解释器内存管理的线程安全

    缺点:同一进程内所有线程同一时刻只能有一个执行,也就是Cpython解释器的多线程无法实现并行

5、有了GIL存在,同一进程内所有线程同一时刻只能有一个执行

6、进程可以利用多核,但是开销大 python多线程开销小,但无法利用多核优势 --> 是不是意为python无用? --> 当然不是

分析
①cpu是用来计算的
②多cpu,可以有多个核并行完成计算,提升计算效率
③每一个cpu遇到io阻塞时,需要等待,多核对提升io无用
结论:对于计算来说,cpu越多越好。对于io来说,再多cpu也无用
案例:
四个任务玩出并发效果
方案一:开四个进程
方案二:一个进程下,开四个线程 单核:计算密集型:--> 方案一开销大 -->方案二胜
io密集型:--> 方案一开销大 -->方案二胜 多核:计算密集型:--> 多核提升计算 --> 方案一胜
io密集型:--> 再多核也无用 --> 方案二胜 总结:#结论:现在的计算机基本上都是多核,python对于计算密集型的任务开多线程的效率并不能带来多大性能上的提升,
甚至不如串行(没有大量切换),但是,对于IO密集型的任务效率还是有显著提升的
问题:python多线程是不是就没有用了呢?
四个任务:计算密集的任务 每个任务耗时10s
单核情况下:
多线程好一点,消耗的资源少一点
多核情况下:
开四个进程:10s多一点
开四个线程:40s多一点 四个任务:IO密集的任务 每个任务io 10s
单核情况下:
多线程好一点
多核情况下: 、
多线程好一点
多线程和多进程都有自己的优点,要根据项目需求合理选择

7、多线程性能测试

①计算密集型

from multiprocessing import Process
from threading import Thread
import os, time def work():
res = 0
for i in range(100):
res *= i if __name__ == '__main__':
l = []
print(os.cpu_count()) # 本机为4核
start = time.time()
for i in range(4):
p = Process(target=work) # 耗时0.7540433406829834
# p=Thread(target=work) # 耗时0.0030002593994140625
l.append(p)
p.start()
for p in l:
p.join()
stop = time.time()
print('run time is %s' % (stop - start))

②io密集型

from multiprocessing import Process
from threading import Thread
import threading
import os, time def work():
time.sleep(2) if __name__ == '__main__':
l = []
print(os.cpu_count()) # 本机为4核
start = time.time()
for i in range(100):
# p = Process(target=work) # 耗时11.416653156280518
p = Thread(target=work) # 耗时2.0711185932159424
l.append(p)
p.start()
for p in l:
p.join()
stop = time.time()
print('run time is %s' % (stop - start))

二、GIL与普通锁的对比

1、对于不同的数据,要想保证安全,需要加不同的锁处理

2、GIL并不能保证数据的安全,它是对Cpython解释器加锁,针对的是线程 保证的是同一个进程下多个线程之间的安全

3、要想保证数据的安全,要自己加不同的锁

from threading import Thread, Lock
import time # mutex 互斥
mutex = Lock()
n = 100 def task():
global n mutex.acquire()
tmp = n
time.sleep(0.1)
n = tmp - 1
mutex.release() t_list = []
for i in range(100):
t = Thread(target=task)
t.start()
t_list.append(t) for t in t_list:
t.join()
print(n)
# 结果为 0

三、死锁与递归锁(了解)

from threading import Thread, RLock
import time 自定义锁一次acquire必须对应一次release,不能连续acquire
递归锁可以连续的acquire,每acquire一次计数加一:针对的是第一个抢到我的人 mutexA = mutexB = RLock() # 抢锁之后会有一个计数 抢一次计数加一 针对的是第一个抢到我的人 class MyThead(Thread):
def run(self):
self.func1()
self.func2() def func1(self):
mutexA.acquire()
print('%s 抢到A锁了' % self.name)
mutexB.acquire()
print('%s 抢到B锁了' % self.name)
mutexB.release()
print('%s 释放了B锁' % self.name)
mutexA.release()
print('%s 释放了A锁' % self.name) def func2(self):
mutexB.acquire()
print('%s 抢到了B锁' % self.name)
time.sleep(1)
mutexA.acquire()
print('%s 抢到A锁了' % self.name)
mutexA.release()
print('%s 释放了A锁' % self.name)
mutexB.release()
print('%s 释放了B锁' % self.name) for i in range(100):
t = MyThead()
t.start()

四、信号量Semaphore(了解)

# Semaphore 信号量  (公共卫生间)

from threading import Thread, Semaphore
import time
import random sm = Semaphore(5) # 五个厕所五把锁
# 跟你普通的互斥锁区别在于,普通的互斥锁是独立卫生间,所有人抢一把锁
# 信号量 公共卫生间 有多个坑,所有人抢多把锁
def task(name):
sm.acquire()
print('%s正在蹲坑' % name)
time.sleep(random.randint(1, 3)) # # 模拟蹲坑耗时
sm.release() if __name__ == '__main__':
for i in range(20):
t = Thread(target=task, args=('特种兵%s号' % i,))
t.start()

五、event事件(了解)

from threading import Thread, Event
import time event = Event() # 设置一个事件实例
def light():
print('红灯亮着')
time.sleep(2)
event.set() # 解除阻塞,给我的event发了一个信号
print('绿灯亮着') def car(i):
print('%s 正在等红灯' % i)
event.wait() # 阻塞
print('%s 开车走了' % i)
t1 = Thread(target=light)
t1.start() for i in range(10):
t = Thread(target=car, args=(i,))
t.start()

六、线程queue

import queue

1、普通q
q = queue.Queue(4)
q.put(1)
q.put(2)
q.put(3)
q.put(4) print(q.get())
print(q.get())
print(q.get())
print(q.get())
# 结果为
1
2
3
4 2、先进后出q
q = queue.LifoQueue(4)
q.put(1)
q.put(2)
q.put(3)
q.put(4)
print(q.get())
print(q.get())
print(q.get())
print(q.get())
# 结果为
4
3
2
1

3、优先级q 最小->>>>最大
q = queue.PriorityQueue()
q.put((10, 'a'))
q.put((-20, 'b'))
q.put((3000, 'c'))
q.put((100, 'd')) print(q.get())
print(q.get())
print(q.get())
print(q.get())
# 结果为
(-20, 'b')
(10, 'a')
(100, 'd')
(3000, 'c')

GIL与event事件讲解的更多相关文章

  1. GIL 信号量 event事件 线程queue

    GIL全局解释器锁 官方解释: In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiple n ...

  2. 1.gil全局解释器锁, 2. 死锁与递归锁 3. 信号量 4. Event事件 5. 线程queue

    gil本质就是一把互斥锁,相当于执行权限,每个进程都会存在一把gil,同一进程内的多个线程必须抢到gil 之后才能使用cpython解释器来执行自己的代码,同一进程下的多线程不能并行,但可以实现并发 ...

  3. 并发编程--一堆锁,GIL,同步异步,Event事件

    目录 一堆锁 死锁现象(*****) 递归锁 RLock (了解) 信号量 (了解) GIL(*****) 什么时GIL锁 为什么需要GIL锁 Cpython解释器与GC的问题 GIL锁带来的问题 多 ...

  4. GIL全局解释锁,死锁,信号量,event事件,线程queue,TCP服务端实现并发

    一.GIL全局解释锁 在Cpython解释器才有GIL的概念,不是python的特点 在Cpython解释器中,同一个进程下开启的多线程,同一时刻只能有一个线程执行,无法利用多核优势. 1.GIL介绍 ...

  5. python并发编程-多线程实现服务端并发-GIL全局解释器锁-验证python多线程是否有用-死锁-递归锁-信号量-Event事件-线程结合队列-03

    目录 结合多线程实现服务端并发(不用socketserver模块) 服务端代码 客户端代码 CIL全局解释器锁****** 可能被问到的两个判断 与普通互斥锁的区别 验证python的多线程是否有用需 ...

  6. 并发编程(五)——GIL全局解释器锁、死锁现象与递归锁、信号量、Event事件、线程queue

    GIL.死锁现象与递归锁.信号量.Event事件.线程queue 一.GIL全局解释器锁 1.什么是全局解释器锁 GIL本质就是一把互斥锁,相当于执行权限,每个进程内都会存在一把GIL,同一进程内的多 ...

  7. python基础--GIL全局解释器锁、Event事件、信号量、死锁、递归锁

    ps:python解释器有很多种,最常见的就是C python解释器 GIL全局解释器锁: GIL本质上是一把互斥锁:将并发变成串行,牺牲效率保证了数据的安全 用来阻止同一个进程下的多个线程的同时执行 ...

  8. TCP协议下的服务端并发,GIL全局解释器锁,死锁,信号量,event事件,线程q

    TCP协议下的服务端并发,GIL全局解释器锁,死锁,信号量,event事件,线程q 一.TCP协议下的服务端并发 ''' 将不同的功能尽量拆分成不同的函数,拆分出来的功能可以被多个地方使用 TCP服务 ...

  9. GIL全局解释器锁-死锁与递归锁-信号量-event事件

    一.全局解释器锁GIL: 官方的解释:掌握概念为主 """ In CPython, the global interpreter lock, or GIL, is a m ...

随机推荐

  1. [转帖]Linux网络管理员不得不了解的系统目录/proc/sys/net/(网络配置)

    Linux网络管理员不得不了解的系统目录/proc/sys/net/(网络配置) https://blog.csdn.net/u013485792/article/details/76416836 需 ...

  2. [转帖]基于VIM漏洞CVE-2019-12735的VIM宏后门病毒详解

    基于VIM漏洞CVE-2019-12735的VIM宏后门病毒详解 不明觉厉 只要是人做的东西 就会有bug 就会有安全问题 就看发现bug 或者是发现安全问题 有没有收益了 会用linux的都是比较熟 ...

  3. c#中抽象类和接口的相同点跟区别

    下面是自己写的一个demo,体现抽象类和接口的用法. using System; using System.Collections.Generic; using System.Linq; using ...

  4. Linux mv命令(7)

    mv命令,move的缩写,顾名思义是移动文件的意思.其实就相当于剪切操作,而前面说的cp命令,就是复制粘贴,这两个有什么区别想必不用多说. 基本使用 使用格式 mv 源文件 目标文件 我的根目录下有 ...

  5. 《剑指offer》面试题6 重建二叉树 Java版

    (由一个二叉树的前序和中序序列重建一颗二叉树) 书中方法:我们要重建一棵二叉树,就要不断地找到根节点和根节点的左子结点和右子节点.注意前序序列, 它的第一个元素就是二叉树的根节点,后面的元素分为它的左 ...

  6. 一篇文章看懂Java并发和线程安全(一)

    一.前言 长久以来,一直想剖析一下Java线程安全的本质,但是苦于有些微观的点想不明白,便搁置了下来,前段时间慢慢想明白了,便把所有的点串联起来,趁着思路清晰,整理成这样一篇文章. 二.导读 1.为什 ...

  7. 洛谷 P2868 [USACO07DEC]观光奶牛Sightseeing Cows 题解

    题面 这道题是一道标准的01分数规划: 但是有一些细节可以优化: 不难想到要二分一个mid然后判定图上是否存在一个环S,该环是否满足∑i=1t(Fun[vi]−mid∗Tim[ei])>0 但是 ...

  8. Python 的开始

    现在的 Linux 上一般都自带有 Python 如果没有,那就下载一个 打开 python 在终端中输入 python ,如果出现了和这差不多的 Python 2.7.15+ (default, O ...

  9. SparkML之推荐算法ALS

    参考: SparkML之推荐算法(一)ALS --有个比较详细的讲解,包含blocks使用. Spark ALS源码总结 //TODO 源码,集群尝试.研究blocks使用原理及作用. 官方解释:nu ...

  10. 11、权重残差图、RLE和NUSE

    affyPLM包可以对芯片原始数据进行拟合回归,最后得到芯片权重(Weights)残差(Residuals)图.相对对数表达(RLE,Relative log expression)箱线图.相对标准差 ...