并发编程: GIL锁、GIL与互斥锁区别、进程池与线程池的区别
本文目录:
一、GIL
什么叫GIL
全局解释器锁(GIL Global Interpreter Lock),锁就是线程里面那个锁,锁是为了避免资源竞争造成数据的错乱,python程序的执行过程,1.启动解释器进程 python.exe,
2.解析你的py文件并执行它,每个py程序中都必须有解释器参与解释器其实就是一堆代码,相当于多个线程要调用同一个解释器代码,共享以为竞争 ,竞争就要出事,所以给解释器加锁。
python 中内存管理依赖于 GC(一段用于回收内存的代码) 也需要一个线程
除了你自己开的线程 系统还有一些内置线程 就算你的代码不会去竞争解释器 内置线程也可能会竞争
所以必须加上锁 当一个线程遇到了IO 同时解释器也会自动解锁 去执行其他线程 CPU会切换到其他程序 x = obj +1
a = obj +1 2 x = None -1
a = None -1 0
二、关于GIL性能的讨论
"""
解释器加锁以后
将导致所有线程只能并发 不能达到真正的并行 意味着同一时间只有一个CPU在处理你的线程
给你的感觉是效率低 代码执行有两种状态
阻塞 i/o 失去CPU的执行权 (CPU等待IO完成)
非阻塞 代码正常执行 比如循环一千万次 中途CPU可能切换 很快会回来 (CPU在计算) 假如有32核CPU 要处理一个下载任务 网络速度慢 100k/s 文件大小为1024kb
如果你的代码中IO操作非常多 cpu性能不能直接决定你的任务处理速度 案例:
目前有三个任务 每个任务处理需一秒 获取元数据需要一小时
3个CPU 需要 一小时1秒
1个cpu 需要 一小时3秒 在IO密集的程序中 CPU性能无法直接决定程序的执行速度 python就应该干这种活儿
在计算密集的程序中 CPU性能可以直接决定程序的执行速度 """
三、计算密集测试
from threading import Thread
from multiprocessing import Process
import time # 计算密集任务 def task1():
sum = 1
for i in range(10000000):
sum *= i def task2():
sum = 1
for i in range(10000000):
sum *= i def task3():
sum = 1
for i in range(10000000):
sum *= i def task4():
sum = 1
for i in range(10000000):
sum *= i def task5():
sum = 1
for i in range(10000000):
sum *= i def task6():
sum = 1
for i in range(10000000):
sum *= i if __name__ == '__main__': # 开始时间
st_time = time.time()
# 多线程情况下
# t1 = Thread(target=task1)
# t2 = Thread(target=task2)
# t3 = Thread(target=task3)
# t4 = Thread(target=task4)
# t5 = Thread(target=task5)
# t6 = Thread(target=task6) t1 = Process(target=task1)
t2 = Process(target=task2)
t3 = Process(target=task3)
t4 = Process(target=task4)
t5 = Process(target=task5)
t6 = Process(target=task6) t1.start()
t2.start()
t3.start()
t4.start()
t5.start()
t6.start()
#
# t1.join()
# t2.join()
# t3.join()
# t4.join()
# t5.join()
# t6.join() print(time.time() - st_time)
四、IO密集测试
from threading import Thread
from multiprocessing import Process
import time # 计算密集任务
def task1():
time.sleep(3) def task2():
time.sleep(3) def task3():
time.sleep(3) def task4():
time.sleep(3) def task5():
time.sleep(3) def task6():
time.sleep(3) if __name__ == '__main__': # 开始时间
st_time = time.time()
# 多线程情况下
# t1 = Thread(target=task1)
# t2 = Thread(target=task2)
# t3 = Thread(target=task3)
# t4 = Thread(target=task4)
# t5 = Thread(target=task5)
# t6 = Thread(target=task6) t1 = Process(target=task1)
t2 = Process(target=task2)
t3 = Process(target=task3)
t4 = Process(target=task4)
t5 = Process(target=task5)
t6 = Process(target=task6) t1.start()
t2.start()
t3.start()
t4.start()
t5.start()
t6.start() # t1.join()
# t2.join()
# t3.join()
# t4.join()
# t5.join()
# t6.join() print(time.time() - st_time)
五、GIL与互斥锁
from threading import Thread,Lock
import time mutex = Lock()
num = 1
def task():
global num
# print(num)
mutex.acquire()
temp = num
print(temp)
time.sleep(1) # 当你们线程中出现io时 GIL锁就解开
num = temp + 1
mutex.release() # 线程任务结束时GIL锁解开 t1 = Thread(target=task,) t2 = Thread(target=task,)
t1.start()
t2.start()
t1.join()
t2.join()
print(num) #
# GIL 和 自定义互斥锁的区别
# 全局锁不能保证自己开启的线程安全 但是保证解释器中的数据的安全的
# GIL 在线程调用解释器时 自动加锁 在IO阻塞时或线程代码执行完毕时 自动解锁
#
六、进程池
"""
进程池
就是一个装进程的容器
为什么出现
当进程很多的时候方便管理进程
什么时候用?
当并发量特别大的时候 列入双十一
很多时候进程是空闲的 就让他进入进程池 让有任务处理时才从进程池取出来使用
进程池使用
ProcessPoolExecutor类
创建时指定最大进程数 自动创建进程
调用submit函数将任务提交到进程池中
创建进程是在调用submit后发生的 总结一下:
进程池可以自动创建进程
进程限制最大进程数
自动选择一个空闲的进程帮你处理任务
遗留问题 进程什么时候算是空闲 """
import socket
from multiprocessing import Process from concurrent.futures import ProcessPoolExecutor # 收发数据
def task(c, addr):
while True:
try:
data = c.recv(1024)
print(data.decode("utf-8"))
if not data:
c.close()
break
c.send(data.upper())
except Exception:
print("连接断开")
c.close()
break if __name__ == '__main__': server = socket.socket() server.bind(("127.0.0.1",65535)) server.listen(5) # 创建一个进程池 默认为CPU个数
pool = ProcessPoolExecutor() while True:
c,addr = server.accept()
# p = Process(target=task,args=(c,addr))
# p.start()
pool.submit(task,c,addr)
七、进程什么时候算是空闲
from concurrent.futures import ProcessPoolExecutor import os,time,random def task():
time.sleep(random.randint(1,2))
print(os.getpid()) def run():
pool = ProcessPoolExecutor(2)
for i in range(30):
pool.submit(task) if __name__ == '__main__':
run()
八、线程池
from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor
from threading import current_thread import os,time,random def task():
time.sleep(random.randint(1,2))
print(current_thread()) def run():
# 默认为cpu核心数 * 5
pool = ThreadPoolExecutor(3)
for i in range(30):
pool.submit(task) if __name__ == '__main__':
run()
并发编程: GIL锁、GIL与互斥锁区别、进程池与线程池的区别的更多相关文章
- python并发编程之多进程(二):互斥锁(同步锁)&进程其他属性&进程间通信(queue)&生产者消费者模型
一,互斥锁,同步锁 进程之间数据不共享,但是共享同一套文件系统,所以访问同一个文件,或同一个打印终端,是没有问题的, 竞争带来的结果就是错乱,如何控制,就是加锁处理 part1:多个进程共享同一打印终 ...
- Java并发编程实战(3)- 互斥锁
我们在这篇文章中主要讨论如何使用互斥锁来解决并发编程中的原子性问题. 目录 概述 互斥锁模型 互斥锁简易模型 互斥锁改进模型 Java世界中的互斥锁 synchronized中的锁和锁对象 synch ...
- java并发编程实战《三》互斥锁(上)
互斥锁(上):解决原子性问题 原子性问题的源头是线程切换,操作系统做线程切换是依赖 CPU 中断的,所以禁止 CPU 发生中断就能够禁止线程切换. 在早期单核 CPU 时代,这个方案的确是可行的,而且 ...
- java并发编程实战《四》互斥锁(下)
互斥锁(下):如何用一把锁保护多个资源? 一把锁可以保护多个资源,但是不能用多把锁来保护一个资源. 那如何保护多个资源? 当我们要保护多个资源时,首先要区分这些资源是否存在关联关系. 如下代码 ...
- Python并发编程05 /死锁现象、递归锁、信号量、GIL锁、计算密集型/IO密集型效率验证、进程池/线程池
Python并发编程05 /死锁现象.递归锁.信号量.GIL锁.计算密集型/IO密集型效率验证.进程池/线程池 目录 Python并发编程05 /死锁现象.递归锁.信号量.GIL锁.计算密集型/IO密 ...
- GIL与普通互斥锁区别,死锁现象,信号量,event事件,进程池与线程池,协程
GIL与普通互斥锁区别 GIL锁和互斥锁的异同点 相同: 都是为了解决解释器中多个线程资源竞争的问题 异: 1.互斥锁是Python代码层面的锁,解决Python程序中多线程共享资源的问题(线程数据共 ...
- GIL锁、进程池与线程池
1.什么是GIL? 官方解释: ''' In CPython, the global interpreter lock, or GIL, is a mutex that prevents multip ...
- GIL锁、进程池与线程池、同步异步
GIL锁定义 GIL锁:Global Interpreter Lock 全局解释器 本质上是一把互斥锁 官方解释: 在CPython中,这个全局解释器锁,也称为GIL,是一个互斥锁,防止多个线程在同 ...
- GIL解释锁及进程池和线程池
官方介绍 ''' 定义: In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiple nati ...
- GIL全局解释器锁、死锁现象、python多线程的用处、进程池与线程池理论
昨日内容回顾 僵尸进程与孤儿进程 # 僵尸进程: 所有的进程在运行结束之后并不会立刻销毁(父进程需要获取该进程的资源) # 孤儿进程: 子进程正常运行 但是产生该子进程的父进程意外死亡 # 守护进程: ...
随机推荐
- yii findAll方法
$users = $this->user->find(array( 'select'=>array('id','username','email'), 'order' => ' ...
- Spring Boot开发Web应用之JSP篇
前言 上一篇介绍了Spring Boot中使用Thymeleaf模板引擎,今天来介绍一下如何使用SpringBoot官方不推荐的jsp,虽然难度有点大,但是玩起来还是蛮有意思的. 正文 先来看看整体的 ...
- 状态管理之 Flux、Redux、Vuex、MobX(概念篇)
本文是对 Flux.Redux.Vuex.MobX 几种常用状态管理模式的总结,偏向于概念层面,不涉及过多代码. 状态管理 什么是状态管理? 状态管理就是,把组件之间需要共享的状态抽取出来,遵循特定的 ...
- 删除指定路径下固定格式,以.log结尾、三天前的文件,或删除空的日志文件
师出‘百测’besttest 删除指定路径下固定格式,以.log结尾.三天前的文件,或删除空的日志文件. 日志文件格式:XXXX_2019-01-01.log. import os,datetime ...
- boostrap table接收到后台返回的数据格式不一致的解决方法
后台使用的分页插件是mybatis的PageHelper,返回数据格式如下: 而bootstrap table插件需要服务端返回的数据格式中含有"total"和"rows ...
- 学习笔记:CentOS7学习之十四:linux文件系统
目录 1. 机械硬盘结构 1.1 机械硬盘结构 1.2 簇和block 2.文件系统结构 2.1 文件名 2.2 inode的内容 2.3 inode的大小 2.4 目录文件 2.5 block块大小 ...
- Design Phone Directory
Design a Phone Directory which supports the following operations: get: Provide a number which is not ...
- 小记--------sparksql和DataFrame的小小案例java、scala版本
sparksql是spark中的一个模块,主要用于进行结构化数据的处理,他提供的最核心的编程抽象,就是DataFrame.同时,sparksql还可以作为分布式的sql查询引擎. 最最重要的功能就是从 ...
- #import "msado15.dll" no_namespace rename("EOF","adoEOF")
引入动态链接库msado15.dll的信息, no_namespace--无命名空间 rename(“EOF”,“adoEOF”)--把文件结束符“EOF”,更换成“adoEOF”
- PAT B1011 A+B 和 C (15)
AC代码 #include <cstdio> int main() { int T, tcase = 1; scanf("%d", &T); for(int i ...