GIL全局解释器锁

官方解释:
In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiple
native threads from executing Python bytecodes at once. This lock is necessary mainly
because CPython’s memory management is not thread-safe.
大译:
python解释器有很多种  最常见的就是Cpython解释器
GIL本质也是一把互斥锁:将并发变成串行牺牲效率保证数据的安全
用来阻止同一个进程下的多个线程的同时执行(同一个进程内多个线程无法实现并行但是可以实现并发)
GIL的存在是因为CPython解释器的内存管理不是线程安全的,这种不安全是来自于垃圾回收机制,每个进程下都会有一个内存管理线程

在一个python的进程内,不仅有test.py的主线程或者由该主线程开发的其他线程,还有解释器开启的来及回收等解释器级别的线程,总之,所有线程都运行在这一个进程内.

所有线程的任务,都需要将代码传给解释器去执行,所以首先要解决的是能够访问到解释器代码
如果多个线程的target = work name执行流程是:

多个线程先访问到解释器的代码,即拿到执行权限,然后将target的代码交给解释器的代码执行,解释器的代码是所有线程共享的,所以来及回收线程也可能访问到解释器的代码而去执行,
这就导致了一个问题:对于共一个数据 100,在线程1执行x = 100的同时,垃圾回收机制执行的是回收100的操作,所以需要通过添加GIL锁,保证python解释器同一时间只能执行一个任务代码

问题: python的多线程没法利用多个优势,是不是就没用了

答: 研究python的多线程是否有用需要分情况讨论

例:

1.四个计算密集型任务, 每个10s

单核情况:

开线程更省资源

多核情况:

开进程 10s

开线程 40s

2.四个IO密集型任务, 每个10s

单核情况:

开线程更省资源

多核情况:

开线程更省资源
from multiprocessing import Process
from threading import Thread
import time, os

# 计算密集型
def func():
res = 0
for i in range(10000):
res *= 1

# IO 密集型
def func():
time.sleep(3)

if __name__ == '__main__':
print(os.cpu_count())
list = []
start = time.time()
for i in range(4):
p = Process(target=func) # 多进程
# 计算密集型运行时间:0.21994638442993164
# IO密集型运行时间:3.2253575325012207
p = Thread(target=func) # 多线程
# 运行时间:0.003988504409790039
# IO密集型运行时间:3.0033791065216064
list.append(p)
p.start()
for p in list:
p.join() # 等待所有子进程/子线程运行结束后再运行主进程/主线程
end = time.time()
print('运行时间:%s'%(end - start))

死锁/递归锁

指的是两个进程或线程在执行的过程中,因争抢资源而造成的一种互相等待的现象.

注意: 自己千万不要轻易处理锁

Rlock 递归锁

Rlock可以被第一个抢到锁的人连续的acquire和release
每acquire一次,锁身上的计数加一
每release一个,锁身上的计数减一
只要锁的计数不为0,其他进程/线程都不能抢
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('%s抢到了A锁'%self.name) # self.name 等价于 current_thread(),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(10):
t = MyThread()
t.start()

# 递归锁
from threading import Thread, RLock

mutexA = mutexB = RLock()

class MyThread(Thread):
def run(self):
self.func1()
self.func2()

def func1(self):
mutexA.acquire()
print('%s抢到了A锁'%self.name) # self.name 等价于 current_thread(),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(10):
t = MyThread()
t.start()

信号量

互斥锁: 一把锁一把钥匙
信号量: 一把锁多把钥匙
from threading import Thread, Semaphore
import time
import random

sem = Semaphore(5) # 生成一把锁五把钥匙

def func(name):
sem.acquire()
print('%s进门了'%name)
time.sleep(random.randint(1, 3))
sem.release()
print('%s出门了'%name)

for i in range(10):
t = Thread(target=func, args=(i, ))
t.start()

event事件

e.set() 发信号
e.wait() 等待信号
from threading import Thread, Event
import time


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)
t.start()

for i in range(10):
t = Thread(target=car, args=('车手%i'%i, ))
t.start()

线程queue

同一个进程下的多个线程本来就是数据共享,为什么还要用队列?
因为队列是 管道+锁,使用队列就不需要自己动手操作锁的问题
import queue

q = queue.Queue()
q.put('one')
q.put('two')
q.put('three')
print(q.get()) # >>> one 先进先出

q = queue.LifoQueue()
q.put('one')
q.put('two')
q.put('three')
print(q.get()) # >>> three 堆栈 先进后出

q = queue.PriorityQueue() # 数字越小,优先级越高
q.put((10, 'one'))
q.put((1, 'two'))
q.put((5, 'three'))
print(q.get()) # >>> (1, 'two')

GIL 信号量 event事件 线程queue的更多相关文章

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

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

  2. 8.14 day32 TCP服务端并发 GIL解释器锁 python多线程是否有用 死锁与递归锁 信号量event事件线程q

    TCP服务端支持并发 解决方式:开多线程 服务端 基础版 import socket """ 服务端 1.要有固定的IP和PORT 2.24小时不间断提供服务 3.能够支 ...

  3. 并发编程---死锁||递归锁---信号量---Event事件---定时器

    死锁 互斥锁:Lock(),互斥锁只能acquire一次 递归锁:  RLock(),可以连续acquire多次,每acquire一次计数器+1,只有计数为0时,才能被抢到acquire # 死锁 f ...

  4. 线程 Thread类 GIL锁 信号量 Event事件

    线程的开启方法 进程是操作系统调度的最小单位,一个进程最少有一个主线程,而一个进程中可以开启多个线程 from threading import Thread def task(): print('A ...

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

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

  6. GIL与event事件讲解

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

  7. (并发编程)RLock(与死锁现象),Semaphore,Even事件,线程Queue

    一.死锁现象与递归锁所谓死锁: 是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去.此时称系统处于死锁状态或系统产生了死锁,这些永远在 ...

  8. 人生苦短之我用Python篇(线程/进程、threading模块:全局解释器锁gil/信号量/Event、)

    线程: 有时被称为轻量级进程(Lightweight Process,LWP),是程序执行流的最小单元.是一串指令的集合.线程是程序中一个单一的顺序控制流程.进程内一个相对独立的.可调度的执行单元,是 ...

  9. Python 36 死锁现象和递归锁、信号量、Event事件、线程queue

    一:死锁现象和递归锁 所谓死锁: 是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去.此时称系统处于死锁状态或系统产生了死锁,这些永远 ...

随机推荐

  1. 转:对JavaScript中闭包的理解

    关于 const     let      var  总结: 建议使用 let  ,而不使用var,如果要声明常量,则用const. ES6(ES2015)出现之前,JavaScript中声明变量只有 ...

  2. Spring MVC <mvc:annotation-driven/>的作用

    一.mvc:annotation-driven的作用 Spring 3.0.x中使用了mvc:annotation-driven后,默认会帮我们注册默认处理请求,参数和返回值的类,其中最主要的两个类: ...

  3. (一)SpringBoot Demo之 Hello World

    文章目录 最终效果 pom文件编写 编写资源类 编写控制器 运行项目 原文 : https://spring.io/guides/gs/rest-service/ 类型:官网入门指南 要求:JDK1. ...

  4. Python函数基础学习(定义、函数参数、递归函数)

    1.本程序是测试函数的基础.函数的参数.递归函数的测试. 函数的参数有: 必选参数.默认参数.可变参数.命名关键字参数和关键字参数 #!/usr/bin/python # -*- coding: ut ...

  5. go语言实现单链表

    线性表包含两种存储方法:顺序存储结构和链式存储结构,其中顺序表的缺点是不便插入与删除数据. 单链表:每个结点包含两部分:数据域+指针域,上一个结点的指针指向下一结点,依次相连,形成链表.特别注意的是每 ...

  6. linux学习之路(一)--centos7安装JDK

    一.卸载centos自带jdk 1.rpm -qa | grep java 查看包含“java”关键字的安装包. 2.然后通过    rpm -e --nodeps   后面跟系统自带的jdk名    ...

  7. Java架构笔记:用JWT对SpringCloud进行认证和鉴权

    写在前面 喜欢的朋友可以关注下专栏:Java架构技术进阶.里面有大量batj面试题集锦,还有各种技术分享,如有好文章也欢迎投稿哦. image.png JWT(JSON WEB TOKEN)是基于RF ...

  8. Redis—简介

    1.Redis是什么? 是一个速度非常快的非关系型数据库,即NoSql数据库(non-relational database) 可以将存储在内存的Key-Value数据持久化到硬盘,可以使用复制特性来 ...

  9. Spark机器学习基础-特征工程

    对连续值处理 0.binarizer/二值化 from __future__ import print_function from pyspark.sql import SparkSession fr ...

  10. 如何让类数组也使用数组的方法比如:forEach()

    思路: 让类数组绑定数组的方法<div>1</div><div>2</div>方法一: let div = document.getElementsBy ...