GIL全局解释锁,死锁,信号量,event事件,线程queue,TCP服务端实现并发
一、GIL全局解释锁
在Cpython解释器才有GIL的概念,不是python的特点
在Cpython解释器中,同一个进程下开启的多线程,同一时刻只能有一个线程执行,无法利用多核优势。
1.GIL介绍
GIL本质就是一把互斥锁,既然是互斥锁,所有互斥锁的本质都是一样,都是将并发运行变成串行,以此来保证数据的安全性。用来阻止同一个进程下的多个线程的同时执行。保护不同的数据的安全,就应该加不同的锁。
每次执行python程序,都会产生一个独立的进程,每个py文件都会产生独立的python进程。在一个python进程中,不仅有这个文件的主线程和开启的其他线程,还有解释器开启的垃圾回收机制等解释器级别的线程。
GIL存在是因为Cpython解释器的内存管理不是线程安全的:(这个内存管理指的是垃圾回收机制)
#1 所有数据都是共享的,这其中,代码作为一种数据也是被所有线程共享的(运行的py文件代码以及Cpython解释器的所有代码)
例如:test.py定义一个函数work,在进程内所有线程都能访问到work的代码,于是我们可以开启三个线程然后target都指向该代码,能访问到意味着就是可以执行。 #2 所有线程的任务,都需要将任务的代码当做参数传给解释器的代码去执行,即所有的线程要想运行自己的任务,首先需要解决的是能够访问到解释器的代码。
综上所述:在同一进程中多个线程在执行代码的时候,多个线程先访问到解释器的代码,拿到执行权限,然后将代码交给解释器去执行。解释器的代码是所有线程共享的,所以垃圾回收线程也是可以可能访问到解释器的代码去执行,这就导致了一个问题,当线程刚刚创建了一个数字100,还没来得及和一个变量名绑定关系,这个时候如果垃圾回收机制来检测到了,会直接把没有绑定关系的数字100删除,考虑到这种问题,所以出现GIL全局解释锁,保证python解释器同一时间只能执行一个任务的代码。

2.GIL和Lock
GIL保护的是解释器级的数据,Lock保护用户自己的数据。保护不同的数据的安全,就应该加不同的锁。
分析:
#1.100个线程去抢GIL锁,即抢执行权限
#2. 肯定有一个线程先抢到GIL(暂且称为线程1),然后开始执行,一旦执行就会拿到lock.acquire()
#3. 极有可能线程1还未运行完毕,就有另外一个线程2抢到GIL,然后开始运行,但线程2发现互斥锁lock还未被线程1释放,于是阻塞,被迫交出执行权限,即释放GIL
#4.直到线程1重新抢到GIL,开始从上次暂停的位置继续执行,直到正常释放互斥锁lock,然后其他的线程再重复2 3 4的过程
GIL锁与互斥锁综合分析(重点!!!)
#互斥锁与join的区别 join是等待某个子线程或者子线程所有代码执行完毕,相当于锁住了整个代码
Lock只是锁住一部分操作数据的代码 两者之间是有很大差距的
3.GIL和多线程
有了GIL的存在,在同一时刻同一进程里面只有一个线程被执行。
那python的多线程没法利用多核的优势,是不是就没用了?
研究python的多线程是否有用需要分情况讨论:
假如有四个任务 计算密集型的情况下
单核情况下:开多个进程只会增加内存的开销,多线程开销更少,线程更节省资源
多核情况下:多核意味着并行计算,多进程可以把任务分散给每一个CPU,但是线程在一个进程中同一时刻只有一个线程执行用不上多核。进程更节省资源
假如有四个任务 I/O密集型的情况下
单核情况下:多进程增加资源消耗,而且进程的切换速度远不如线程,线程更节省资源
多核情况下:进程再多的核也解决不了I/O问题,都会进入阻塞态,而且还消耗资源,线程更节省资源
应用:多线程用于IO密集型:如socket,爬虫,web 多进程用于计算密集型:如金融分析
二、死锁现象和递归锁
所谓死锁:是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。如下就是死锁
from threading import Thread,Lock
import time
mutexA=Lock()
mutexB=Lock() class MyThread(Thread):
def run(self):
self.func1()
self.func2()
def func1(self):
mutexA.acquire()
print('\033[41m%s 拿到A锁\033[0m' %self.name) mutexB.acquire()
print('\033[42m%s 拿到B锁\033[0m' %self.name)
mutexB.release() mutexA.release() def func2(self):
mutexB.acquire()
print('\033[43m%s 拿到B锁\033[0m' %self.name)
time.sleep(2) mutexA.acquire()
print('\033[44m%s 拿到A锁\033[0m' %self.name)
mutexA.release() mutexB.release() if __name__ == '__main__':
for i in range(10):
t=MyThread()
t.start() '''
Thread-1 拿到A锁
Thread-1 拿到B锁
Thread-1 拿到B锁
Thread-2 拿到A锁
然后就卡住,死锁了
'''
解决方法使用递归锁:RLock。RLock可以被第一个抢到锁的人连续的acquire和release,每acquire一次,锁计数加1,每release一次,锁计数减1,只要锁的
计数不为0,其他线程都只能等待,等待该线程释放完所有锁。
三、信号量Semaphore
Semaphore 管理一个内置的计数器,每当调用acquire()是内置计数-1,调用release()是内置计数器+1,计数器不能小于0,当计数器为0时,acquire()将阻塞线程直到其他线程调用release()
"""
互斥锁:一个厕所(一个坑位)
信号量:公共厕所(多个坑位)
"""
from threading import Semaphore,Thread
import time
import random sm = Semaphore(5) # 造了一个含有五个的坑位的公共厕所 def task(name):
sm.acquire()
print('%s占了一个坑位'%name)
time.sleep(random.randint(1,3))
sm.release() for i in range(40):
t = Thread(target=task,args=(i,))
t.start() #同时创建了40个线程,5个坑位的公共厕所,开始有五个线程去占,睡不同时间之后,释放锁,同时其他线程拿到锁,最多只能有五个线程拿到锁。
信号量例子
四、event事件 让线程等待线程
from threading import Event,Thread
import time # 先生成一个event对象
e = Event() def light():
print('红灯正亮着')
time.sleep(3)
e.set() # 发信号
print('绿灯亮了') def car(name):
print('%s正在等红灯'%name)
e.wait() # 等待信号
print('%s加油门飙车了'%name) t = Thread(target=light) #线程运行light函数
t.start() for i in range(10):
t = Thread(target=car,args=('伞兵%s'%i,))
t.start() #线程先执行light函数,然后运行car函数,睡3秒,运行e.set()及下面的代码,同时,e.wait在等待信号,接收到信号之后也执行下面的代码。
五、线程queue
同一个进程下的多个线程本来就是数据共享 为什么还要用队列,因为队列是管道+锁,使用队列你就不需要自己手动操作锁的问题,因为锁操作不好极容易产生死锁现象。
import queue q = queue.Queue() #先进先出
q.put('hahha')
print(q.get()) #haha q = queue.LifoQueue() #last in first out 后进先出 和堆栈先进后出一样
q.put(1)
q.put(2)
q.put(3)
print(q.get()) #3 q = queue.PriorityQueue()
# 数字越小 优先级越高
q.put((10,'haha'))
q.put((100,'hehehe'))
q.put((0,'xxxx'))
q.put((-10,'yyyy'))
print(q.get()) #(-10, 'yyyy')
六、TCP服务端实现并发
将不同的功能尽量拆分成不同的函数拆分出来的功能可以被多个地方使用
1.将连接循环和通信循环拆分成不同的函数
2.将服务端通信循环做成多线程
服务端
import socket
from threading import Thread server = socket.socket()
server.bind(('127.0.0.1',8080))
server.listen(5) def talk(conn):
while True:
try:
data = conn.recv(1024)
if len(data) == 0:break
print(data.decode('utf-8'))
conn.send(data.upper())
except ConnectionResetError as e:
print(e)
break
conn.close() while True:
conn, addr = server.accept() # 监听 等待客户端的连接 阻塞态
t = Thread(target=talk,args=(conn,))
t.start()
客户端
import socket client = socket.socket()
client.connect(('127.0.0.1',8080)) while True:
client.send(b'hello')
data = client.recv(1024)
print(data.decode('utf-8'))
GIL全局解释锁,死锁,信号量,event事件,线程queue,TCP服务端实现并发的更多相关文章
- GIL 信号量 event事件 线程queue
GIL全局解释器锁 官方解释: In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiple n ...
- 8.14 day32 TCP服务端并发 GIL解释器锁 python多线程是否有用 死锁与递归锁 信号量event事件线程q
TCP服务端支持并发 解决方式:开多线程 服务端 基础版 import socket """ 服务端 1.要有固定的IP和PORT 2.24小时不间断提供服务 3.能够支 ...
- 20191031:GIL全局解释锁
20191031:GIL全局解释锁 总结关于GIL全局解释锁的个人理解 GIl全局解释锁,本身不是Python语言的特性,而是Python语言底层的c Python解释器的一个特性.在其他解释器中是没 ...
- ~~并发编程(十一):GIL全局解释锁~~
进击のpython ***** 并发编程--GIL全局解释锁 这小节就是有些"大神"批判python语言不完美之处的开始 这一节我们要了解一下Cpython的GIL解释器锁的工作机 ...
- 子进程回收资源两种方式,僵尸进程与孤儿进程,守护进程,进程间数据隔离,进程互斥锁,队列,IPC机制,线程,守护线程,线程池,回调函数add_done_callback,TCP服务端实现并发
子进程回收资源两种方式 - 1) join让主进程等待子进程结束,并回收子进程资源,主进程再结束并回收资源. - 2) 主进程 “正常结束” ,子进程与主进程一并被回收资源. from multipr ...
- python并发编程-多线程实现服务端并发-GIL全局解释器锁-验证python多线程是否有用-死锁-递归锁-信号量-Event事件-线程结合队列-03
目录 结合多线程实现服务端并发(不用socketserver模块) 服务端代码 客户端代码 CIL全局解释器锁****** 可能被问到的两个判断 与普通互斥锁的区别 验证python的多线程是否有用需 ...
- GIL全局解释器锁-死锁与递归锁-信号量-event事件
一.全局解释器锁GIL: 官方的解释:掌握概念为主 """ In CPython, the global interpreter lock, or GIL, is a m ...
- 10 并发编程-(线程)-GIL全局解释器锁&死锁与递归锁
一.GIL全局解释器锁 1.引子 在Cpython解释器中,同一个进程下开启的多线程,同一时刻只能有一个线程执行,无法利用多核优势 首先需要明确的一点是GIL并不是Python的特性,它是在实现Pyt ...
- python中的GIL(全局解释锁)多线程能够提升效率
预启动的时候,应用程序仍然会调用 OnLaunched 方法的,在 OnLaunched 方法调用之后,会马上发生 Suspending 事件,随后应用就会暂停. 我先基于develop主分支拉出一个 ...
随机推荐
- js中call,apply,bind方法的用法
call .apply.和bind 以上这三个方法都是js function函数当中自带的方法,用来改变当前函数this的指向. call()方法 语法格式: fun.call(thisArg[,ar ...
- RFM模型的变形LRFMC模型与K-means算法的有机结合
应用场景: 可以应用在不同行业的客户分类管理上,比如航空公司,传统的RFM模型不再适用,通过RFM模型的变形LRFMC模型实现客户价值分析:基于消费者数据的精细化营销 应用价值: LRFMC模型构建之 ...
- linux下 调试C#代码
参考两位大神的帖子: https://blog.csdn.net/soband_xiang/article/details/82914195 https://blog.csdn.net/weixin_ ...
- 菜鸟网络Java面试-社招-一面(2019/11)
个人情况 2017年毕业,普通本科,计算机科学与技术专业,毕业后在一个二三线小城市从事Java开发,2年Java开发经验.做过分布式开发,没有高并发的处理经验,平时做To G的项目居多.写下面经是希望 ...
- Golang 接口
1 接口是什么 Golang中没有像Python.Java拥有类和对象的概念,其封装对象或说明对象是通过接口来实现的.比如谁能够实现什么样的功能,便能够将其抽象化封装. 接口定义了一组方法(抽象方法集 ...
- Windows 文件过滤驱动经验总结
Windows 文件过滤驱动经验总结作者:sinister 本文转载自驱动开发网 看了 ChuKuangRen 的第二版<文件过滤驱动开发教程>后,颇有感触.我想,交流都是建立在平等的基础 ...
- [技术博客]Pyqt中View类别容器和Widget类别容器的区别
Pyqt中View类别容器和Widget类别容器的区别 简介 在beta迭代中,我们选择用pyqt5来重写alpha迭代中使用tkinter库编写的界面. 按钮之类的与tkiner使用无异,在显示 ...
- PHP面试-复习知识点整理
false的七种情况 整型0 浮点0.0 布尔false 空字符串'',"" 字符串'0' 空数组[] NULL 超全局数组 $GLOBALS,包含下面8个超全局数组的值 $_GE ...
- 在Asp.Net Core 3.0中如何使用 Newtonsoft.Json 库序列化数据
在.Net Core 3.0中 内置了一套Json序列化/反序列化方案,默认可以不再依赖,不再支持 Newtonsoft.Json. 但是.NET Core 3.0 System.Text.Jso ...
- 检查 chrome 插件是否存在
你必须了解 chrome 插件开发才能阅读以下内容. 传送门: https://qa.1r1g.com/sf/ask/440544891/ 原理:页面 js 向 chrome 插件的 backgrou ...