前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >46.python GIL锁与互斥锁Lock的区别

46.python GIL锁与互斥锁Lock的区别

作者头像
猿说编程[Python和C]
修改2021-04-19 14:30:43
1.7K0
修改2021-04-19 14:30:43
举报
文章被收录于专栏:猿说编程猿说编程

前面的文章分别介绍了python线程互斥锁Lockpython GIL锁,两个对 python线程threading 都会有影响,那么具体又有什么区别呢?

GIL
GIL

一.python线程互斥锁Lock

python中,当有多个线程threading同时执行时,对同一个全局变量或者同一个文件操作时,如果没有设置互斥锁,容易造成数据混乱,比如下面这两个案例:

1.案例一:两个线程对全局变量分别累加1000000次,不加互斥锁,看全局变量的计算结果是否为2000000
代码语言:javascript
复制
# !usr/bin/env python
# -*- coding:utf-8 _*-
"""
@Author:何以解忧
@Blog(个人博客地址): https://www.codersrc.com/
@File:python_thread.py
@Time:2020/1/5 21:22
 
@Motto:不积跬步无以至千里,不积小流无以成江海,程序人生的精彩需要坚持不懈地积累!
"""
# 导入线程threading模块
import threading
 
# 声明全局变量
g_num = 0
 
def my_thread1():
 
    # 声明全局变量
    global g_num
    # 循环 1000000 次,每次累计加 1
    for i in range(0,1000000):
        g_num = g_num + 1
 
def my_thread2():
 
    # 声明全局变量
    global g_num
    # 循环 1000000 次,每次累计加 1
    for i in range(0,1000000):
        g_num = g_num + 1
 
def main(i):
 
    # 声明全局变量
    global g_num
    # 初始化全局变量,初始值为 0
    g_num = 0
    # 创建两个线程,对全局变量进行累计加 1
    t1 = threading.Thread(target=my_thread1)
    t2 = threading.Thread(target=my_thread2)
 
    # 启动线程
    t1.start()
    t2.start()
    # 阻塞函数,等待线程结束
    t1.join()
    t2.join()
    # 获取全局变量的值
    print("第%d次计算结果:%d "% (i,g_num))
 
if __name__ == "__main__":
 
    # 循环4次,调用main函数,计算全局变量的值
    for i in range(1,5):
        main(i)

输出结果:

代码语言:javascript
复制
第1次计算结果:1262996 
第2次计算结果:1661455 
第3次计算结果:1300211 
第4次计算结果:1563699

一脸懵逼的看到计算结果每次都不同,每次都是一个小于2000000的随机数,这是为什么??

茫然
茫然

假如当前 g_num 值是100,当线程1执行第一步时,cpu通过计算获得结果101,并准备把计算的结果101赋值给g_num,然后再传值的过程中,线程2突然开始执行了并且执行了第一步,此时g_num的值仍未100,101还在传递的过程中,还没成功赋值,线程2获得计算结果101,并准备传递给g_num,经过一来一去这么一折腾,分明做了两次加 1 操作,g_num结果却是101,误差就由此产生,往往循环次数越多,产生的误差就越大。

表情
表情
2.案例二:两个线程对全局变量分别累加1000000次,加互斥锁,看全局变量的计算结果是否为2000000
代码语言:javascript
复制
# !usr/bin/env python
# -*- coding:utf-8 _*-
"""
@Author:何以解忧
@Blog(个人博客地址): https://www.codersrc.com/
 
@File:python_thread_lock.py
@Time:2020/1/5 21:22
 
@Motto:不积跬步无以至千里,不积小流无以成江海,程序人生的精彩需要坚持不懈地积累!
"""
# 导入线程threading模块
import threading
 
# 声明全局变量
g_num = 0
# 创建互斥锁
mutex = threading.Lock()
 
def my_thread1():
 
    # 声明全局变量
    global g_num
    # 循环 1000000 次,每次累计加 1
    for i in range(0,1000000):
        # 锁定资源
        mutex.acquire()
        g_num = g_num + 1
        # 解锁资源
        mutex.release()
 
def my_thread2():
 
    # 声明全局变量
    global g_num
    # 循环 1000000 次,每次累计加 1
    for i in range(0,1000000):
        # 锁定资源
        mutex.acquire()
        g_num = g_num + 1
        # 解锁资源
        mutex.release()
 
def main(i):
 
    # 声明全局变量
    global g_num
    # 初始化全局变量,初始值为 0
    g_num = 0
    # 创建两个线程,对全局变量进行累计加 1
    t1 = threading.Thread(target=my_thread1)
    t2 = threading.Thread(target=my_thread2)
 
    # 启动线程
    t1.start()
    t2.start()
    # 阻塞函数,等待线程结束
    t1.join()
    t2.join()
    # 获取全局变量的值
    print("第%d次计算结果:%d "% (i,g_num))
 
if __name__ == "__main__":
 
    # 循环4次,调用main函数,计算全局变量的值
    for i in range(1,5):
        main(i)

输出结果:

代码语言:javascript
复制
第1次计算结果:2000000 
第2次计算结果:2000000 
第3次计算结果:2000000 
第4次计算结果:2000000

由此可见,全局变量计算加上互斥锁之后,不论执行多少次,计算结果都相同。注意:互斥锁一旦锁定之后要记得解锁,否则资源会一直处于锁定状态,容易造成死锁

小心心
小心心

二.python GIL锁

python GIL锁 也称为:全局解释器所(global interpreter lock),当有多个线程同时执行时,每个线程在执行时候都需要先获取GIL,保证同一时刻只有一个线程可以执行代码,即同一时刻只有一个线程使用CPU,也就是说多线程并不是真正意义上的同时执行!

任何Python  线程threading 执行前,必须先获得GIL锁才能执行,当线程获取到GIL锁之后,每执行100条字节码,解释器就自动释放GIL锁,让别的线程有机会执行。

摇耳朵
摇耳朵

三.python互斥锁Lock与GIL锁的关系

首先假设只有一个进程,这个进程中有两个线程 Thread1,Thread2, 要修改共享的数据date, 并且有互斥锁,执行以下步骤:

(1)多线程运行,假设Thread1获得GIL可以使用cpu,这时Thread1获得 互斥锁lock,Thread1可以改date数据(但并没有开始修改数据);

(2)Thread1线程在修改date数据前发生了 i/o操作 或者 ticks计数满100 (注意就是没有运行到修改data数据),这个时候 Thread1 让出了Gil,Gil锁可以被竞争;

(3) Thread1 和 Thread2 开始竞争 Gil (注意:如果Thread1是因为 i/o 阻塞 让出的Gil Thread2必定拿到Gil,如果Thread1是因为ticks计数满100让出Gil 这个时候 Thread1 和 Thread2 公平竞争);

(4)假设 Thread2正好获得了GIL, 运行代码去修改共享数据date,由于Thread1有互斥锁lock,所以Thread2无法更改共享数据date,这时Thread2让出Gil锁 , GIL锁再次发生竞争;

(5)假设Thread1又抢到GIL,由于其有互斥锁Lock所以其可以继续修改共享数据data,当Thread1修改完数据释放互斥锁lock,Thread2在获得GIL与lock后才可对data进行修改;

以上描述了 互斥锁和Gil锁的 一个关系

猜你喜欢:

1.python线程threading

2.python GIL锁

3.python进程Process

4.python进程Process与线程threading区别

转载请注明猿说Python » python GIL锁与互斥锁Lock的区别

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-01-11 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一.python线程互斥锁Lock
    • 1.案例一:两个线程对全局变量分别累加1000000次,不加互斥锁,看全局变量的计算结果是否为2000000
      • 2.案例二:两个线程对全局变量分别累加1000000次,加互斥锁,看全局变量的计算结果是否为2000000
      • 二.python GIL锁
      • 三.python互斥锁Lock与GIL锁的关系
        • 猜你喜欢:
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档