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多线程锁机制的更多相关文章

  1. python基础之多线程锁机制

    GIL(全局解释器锁) GIL并不是Python的特性,它是在实现Python解析器(CPython)时所引入的一个概念,是为了实现不同线程对共享资源访问的互斥,才引入了GIL 在Cpython解释器 ...

  2. Python高阶之多线程锁机制

    '''1.多进程的优势:为了同步完成多项任务,通过提高资源使用效率来提高系统的效率.2.查看线程数:threading.enumerate()函数便可以看到当前线程的数量.3.查看当前线程的名字:th ...

  3. Python开发基础-Day29多线程

    概念 进程:进程就是一个程序在一个数据集上的一次动态执行过程 程序:代码 数据集:程序执行过程中需要的资源 进程控制块:完成状态保存的单元 线程:线程是寄托在进程之上,为了提高系统的并发性 线程是进程 ...

  4. python 多线程锁机制

    GIL(全局解释器锁) GIL并不是Python的特性,它是在实现Python解析器(CPython)时所引入的一个概念,是为了实现不同线程对共享资源访问的互斥,才引入了GIL 在Cpython解释器 ...

  5. [java多线程] - 锁机制&同步代码块&信号量

    在美眉图片下载demo中,我们可以看到多个线程在公用一些变量,这个时候难免会发生冲突.冲突并不可怕,可怕的是当多线程的情况下,你没法控制冲突.按照我的理解在java中实现同步的方式分为三种,分别是:同 ...

  6. Python开发基础-Day32 进程间通信、进程池、协程

    进程间通信 进程彼此之间互相隔离,要实现进程间通信(IPC),multiprocessing模块支持两种形式:队列和管道,这两种方式都是使用消息传递的. 进程队列queue 不同于线程queue,进程 ...

  7. 还在用Alpine作为你Docker的Python开发基础镜像?其实Ubuntu更好一点

    原文转载自「刘悦的技术博客」https://v3u.cn/a_id_173 一般情况下,当你想为你的Python开发环境选择一个基础镜像时,大多数人都会选择Alpine,为什么?因为它太小了,仅仅只有 ...

  8. python开发进程:互斥锁(同步锁)&进程其他属性&进程间通信(queue)&生产者消费者模型

    一,互斥锁,同步锁 进程之间数据不共享,但是共享同一套文件系统,所以访问同一个文件,或同一个打印终端,是没有问题的, 竞争带来的结果就是错乱,如何控制,就是加锁处理 part1:多个进程共享同一打印终 ...

  9. Python开发基础-Day31 Event对象、队列和多进程基础

    Event对象 用于线程间通信,即程序中的其一个线程需要通过判断某个线程的状态来确定自己下一步的操作,就用到了event对象 event对象默认为假(Flase),即遇到event对象在等待就阻塞线程 ...

随机推荐

  1. [洛谷P4609] [FJOI2016]建筑师

    洛谷题目链接:[FJOI2016]建筑师 题目描述 小 Z 是一个很有名的建筑师,有一天他接到了一个很奇怪的任务:在数轴上建 \(n\) 个建筑,每个建筑的高度是 \(1\) 到 \(n\) 之间的一 ...

  2. 利用ChainMap进行多字典合并

    aa = [{105199: 'https://picx0.jpeg'}, {105187: 'https://picx1.jpeg'}, {105170: 'https:picx2.jpeg'}, ...

  3. Jeson老师写的nginx切割脚本

    #Jeson #Email:jeson@iaskjob.com #变量定义:access.error日志文件列表 NGINX_LOG=(imoocc_com_access iaskjob_com er ...

  4. 从零搭建SSM框架(五)Maven实现Tomcat热部署

    配置tomcat 第一步:需要修改tomcat的conf/tomcat-users.xml配置文件.添加用户名.密码.权限. <role rolename="manager-gui&q ...

  5. 转:国内优秀npm镜像推荐及使用

    原文:http://riny.net/2014/cnpm/ npm全称Node Package Manager,是node.js的模块依赖管理工具.由于npm的源在国外,所以国内用户使用起来各种不方便 ...

  6. 1030 大数进制转换(51Nod + JAVA)

    题目链接:https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1030 题目: 代码实现如下: import java.mat ...

  7. vue安装说明

    1.安装node.js(http://www.runoob.com/nodejs/nodejs-install-setup.html) --以下操作在nodejs安装路径下进行(记得不要在C盘)-- ...

  8. python OS 模块 文件目录操作

    Python OS 模块 文件目录操作 os模块中包含了一系列文件操作的函数,这里介绍的是一些在Linux平台上应用的文件操作函数.由于Linux是C写的,低层的libc库和系统调用的接口都是C AP ...

  9. python 并发爬虫的快感

    import time from tomorrow import threads from requests_html import HTMLSession session=HTMLSession() ...

  10. 【51nod1006】simple KMP

    原题意看的挺迷糊的,后来看了http://blog.csdn.net/YxuanwKeith/article/details/52351335大爷的题意感觉清楚的多…… 做法也非常显然了,用树剖维护后 ...