Python开发基础-Day30多线程锁机制
GIL(全局解释器锁)
GIL并不是Python的特性,它是在实现Python解析器(CPython)时所引入的一个概念,是为了实现不同线程对共享资源访问的互斥,才引入了GIL
在Cpython解释器中,同一个进程下开启的多线程,同一时刻只能有一个线程执行,无法利用多核优势

python对于计算密集型的任务开多线程的效率甚至不如串行(没有大量切换),但是,对于IO密集型的任务效率还是有显著提升的。
GIL原理图

计算密集型:结果肯定是100,因为每一次start结果就已经出来了,所以第二个线程肯定是通过调用第一个线程的count值进行计算的
def sub():
global count '''线程的公共数据 下'''
temp=count
count=temp+1
'''线程的公共数据 上''' time.sleep(2)
count=0 l=[]
for i in range(100):
t=threading.Thread(target=sub,args=())
t.start() #每一次线程激活,申请一次gillock
l.append(t)
for t in l:
t.join()
print(count)
io密集型:当第一个线程开始start的时候,由于sleep了0.001秒,这0.001秒对于人而言很短,但是对于cpu而言,这0.001秒已经做了很多的事情了,在这里cpu做的事情就是或许已经start了100个线程,所以导致大多数的线程调用的count值还是0,即temp=0,只有少数的线程完成了count=temp+1的操作,所以输出的count结果不确定,可能是7、8、9,也可能是10几。
def sub():
global count '''线程的公共数据 下'''
temp=count
time.sleep(0.001) #大量的io操作
count=temp+1
'''线程的公共数据 上''' time.sleep(2)
count=0 l=[]
for i in range(100):
t=threading.Thread(target=sub,args=())
t.start()
l.append(t)
for t in l:
t.join()
print(count)
注意以下的锁都是多线程提供的锁机制,与python解释器引入的gil概念无关
互斥锁(同步锁)
互斥锁是用来解决上述的io密集型场景产生的计算错误,即目的是为了保护共享的数据,同一时间只能有一个线程来修改共享的数据。
def sub():
global count
lock.acquire() #上锁,第一个线程如果申请到锁,会在执行公共数据的过程中持续阻塞后续线程
#即后续第二个或其他线程依次来了发现已经被上锁,只能等待第一个线程释放锁
#当第一个线程将锁释放,后续的线程会进行争抢 '''线程的公共数据 下'''
temp=count
time.sleep(0.001)
count=temp+1
'''线程的公共数据 上''' lock.release() #释放锁
time.sleep(2)
count=0 l=[]
lock=threading.Lock() #将锁内的代码串行化
for i in range(100):
t=threading.Thread(target=sub,args=())
t.start()
l.append(t)
for t in l:
t.join()
print(count)
死锁
保护不同的数据就应该加不同的锁。
所以当有多个互斥锁存在的时候,可能会导致死锁,死锁原理如下:
import threading
import time
def foo():
lockA.acquire()
print('func foo ClockA lock')
lockB.acquire()
print('func foo ClockB lock')
lockB.release()
lockA.release() def bar(): lockB.acquire()
print('func bar ClockB lock')
time.sleep(2) # 模拟io或者其他操作,第一个线程执行到这,在这个时候,lockA会被第二个进程占用
# 所以第一个进程无法进行后续操作,只能等待lockA锁的释放
lockA.acquire()
print('func bar ClockA lock')
lockB.release()
lockA.release() def run():
foo()
bar() lockA=threading.Lock()
lockB=threading.Lock()
for i in range(10):
t=threading.Thread(target=run,args=())
t.start() 输出结果:只有四行,因为产生了死锁阻断了
func foo ClockA lock
func foo ClockB lock
func bar ClockB lock
func foo ClockA lock
递归锁(重要)
解决死锁
import threading
import time
def foo():
rlock.acquire()
print('func foo ClockA lock')
rlock.acquire()
print('func foo ClockB lock')
rlock.release()
rlock.release() def bar():
rlock.acquire()
print('func bar ClockB lock')
time.sleep(2)
rlock.acquire()
print('func bar ClockA lock')
rlock.release()
rlock.release() def run():
foo()
bar() rlock=threading.RLock() #RLock本身有一个计数器,如果碰到acquire,那么计数器+1
#如果计数器大于0,那么其他线程无法查收,如果碰到release,计数器-1 for i in range(10):
t=threading.Thread(target=run,args=())
t.start()
Semaphore(信号量)
实际上也是一种锁,该锁用于限制线程的并发量
以下代码在sleep两秒后会打印出100个ok
import threading
import time
def foo():
time.sleep(2)
print('ok') for i in range(100):
t=threading.Thread(target=foo,args=())
t.start()
每2秒打印5次ok
import threading
import time
sem=threading.Semaphore(5)
def foo():
sem.acquire()
time.sleep(2)
print('ok')
sem.release() for i in range(100):
t=threading.Thread(target=foo,args=())
t.start()
Python开发基础-Day30多线程锁机制的更多相关文章
- python基础之多线程锁机制
GIL(全局解释器锁) GIL并不是Python的特性,它是在实现Python解析器(CPython)时所引入的一个概念,是为了实现不同线程对共享资源访问的互斥,才引入了GIL 在Cpython解释器 ...
- Python高阶之多线程锁机制
'''1.多进程的优势:为了同步完成多项任务,通过提高资源使用效率来提高系统的效率.2.查看线程数:threading.enumerate()函数便可以看到当前线程的数量.3.查看当前线程的名字:th ...
- Python开发基础-Day29多线程
概念 进程:进程就是一个程序在一个数据集上的一次动态执行过程 程序:代码 数据集:程序执行过程中需要的资源 进程控制块:完成状态保存的单元 线程:线程是寄托在进程之上,为了提高系统的并发性 线程是进程 ...
- python 多线程锁机制
GIL(全局解释器锁) GIL并不是Python的特性,它是在实现Python解析器(CPython)时所引入的一个概念,是为了实现不同线程对共享资源访问的互斥,才引入了GIL 在Cpython解释器 ...
- [java多线程] - 锁机制&同步代码块&信号量
在美眉图片下载demo中,我们可以看到多个线程在公用一些变量,这个时候难免会发生冲突.冲突并不可怕,可怕的是当多线程的情况下,你没法控制冲突.按照我的理解在java中实现同步的方式分为三种,分别是:同 ...
- Python开发基础-Day32 进程间通信、进程池、协程
进程间通信 进程彼此之间互相隔离,要实现进程间通信(IPC),multiprocessing模块支持两种形式:队列和管道,这两种方式都是使用消息传递的. 进程队列queue 不同于线程queue,进程 ...
- 还在用Alpine作为你Docker的Python开发基础镜像?其实Ubuntu更好一点
原文转载自「刘悦的技术博客」https://v3u.cn/a_id_173 一般情况下,当你想为你的Python开发环境选择一个基础镜像时,大多数人都会选择Alpine,为什么?因为它太小了,仅仅只有 ...
- python开发进程:互斥锁(同步锁)&进程其他属性&进程间通信(queue)&生产者消费者模型
一,互斥锁,同步锁 进程之间数据不共享,但是共享同一套文件系统,所以访问同一个文件,或同一个打印终端,是没有问题的, 竞争带来的结果就是错乱,如何控制,就是加锁处理 part1:多个进程共享同一打印终 ...
- Python开发基础-Day31 Event对象、队列和多进程基础
Event对象 用于线程间通信,即程序中的其一个线程需要通过判断某个线程的状态来确定自己下一步的操作,就用到了event对象 event对象默认为假(Flase),即遇到event对象在等待就阻塞线程 ...
随机推荐
- hadoop之存储篇
---持续更新中,可留言讨论--- --题目导航见页面左上角的悬浮框#目录导航#-- 一.目录: 集群规划 HDFS HA 冒烟测试 功能特性 二.集群规划: 负载类型 容量规划 可扩展性 角色分离 ...
- spring bean初始化及销毁你必须要掌握的回调方法
spring bean在初始化和销毁的时候我们可以触发一些自定义的回调操作. 初始化的时候实现的方法 1.通过java提供的@PostConstruct注解: 2.通过实现spring提供的Initi ...
- 数组C - 玛雅日历
During his last sabbatical, professor M. A. Ya made a surprising discovery about the old Maya calend ...
- HDU 1711 Number Sequence (字符串处理 KMP)
题目链接 Problem Description Given two sequences of numbers : a[1], a[2], ...... , a[N], and b[1], b[2], ...
- MSSQL 基础知识002
---启用sa账号 1. 先使用一个windows账号登陆. 2.在数据库实例上面右键,属性,安全性,登录名,sa. 右键,属性. 常规,修改sa的密码. 状态,启用sa账号. 主键的作用: 1.唯一 ...
- 144.Binary Tree Preorder Traversal---二叉树先序、中序非递归遍历
题目链接 题目大意:返回二叉树的先序遍历list.中序见94,后序见145. 法一:普通递归遍历,只是这里多了一个list数组,所以分成了两个函数.代码如下(耗时1ms): public List&l ...
- ERROR in vc 6.0 (LINK : fatal error LNK1561: entry point must be defined)
导致错误 LINK : fatal error LNK1561: entry point must be defined 的原因有很多种, 网上可以搜到很多, 一般是函数入口没定义, 或者修改为/su ...
- Redis安装和客户端cli常见操作
安装Redis $ wget http://download.redis.io/releases/redis-4.0.6.tar.gz $ tar xzf redis-4.0.6.tar.gz $ c ...
- Nginx源码分析-ngx_module_s结构体
该结构体是整个Nginx模块化架构最基本的数据结构体.它描述了Nginx程序中一个模块应该包括的基本属性,在tengine/src/core/ngx_conf_file.h中定义了该结构体 struc ...
- Groovy 与 DSL
一:DSL 概念 指的是用于一个特定领域的语言(功能领域.业务领域).在这个给出的概念中有 3个重点: 只用于一个特定领域,而非所有通用领域,比如 Java / C++就是用于通用领域,而不可被称为 ...