day9-为什么需要线程锁(互斥锁)

一、概述

   线程需要沟通,需要共享数据,但是我们之前并没有涉及到多线程情况共享数据的例子。下面我们就来探讨一下,多线程共享数据会出现什么情况。这边就需要用到线程锁,又叫互斥锁(mutex)。

二、线程锁(互斥锁)

2.1、前戏

说明:我们现在来探讨多线程数据共享的情况

import threading,time

def run(n):
    global num   #把num变成全局变量
    time.sleep(1)  #注意了sleep的时候是不占有cpu的,这个时候cpu直接把这个线程挂起了,此时cpu去干别的事情去了
    num += 1   #所有的线程都做+1操作

num = 0   #初始化num为0
t_obj = list()
for i in range(100):
    t = threading.Thread(target=run,args=("t-{0}".format(i),))
    t.start()
    t_obj.append(t)

for t in t_obj:
    t.join()


print("--------all thead has finished")
print("num:",num)   #输出最后的num值

#输出
--------all thead has finished
('num:', 97)  #输出的结果

   这个时候有些小伙伴就说了,你最后输出的结果怎么会是 97 呢?应该是100才对啊,不是有GIL(全局解释器锁)已经控制了,这个到底是为什么呐?

答:注意了,这种情况只能在python2.x 中才会出现的,python3.x里面没有这种现象。下面我们就用一张图来解释一下这个原因。如图:

 

图解释:

  1. 到第5步的时候,可能这个时候python正好切换了一次GIL(据说python2.7中,每100条指令会切换一次GIL),执行的时间到了,被要求释放GIL,这个时候thead 1的count=0并没有得到执行,而是挂起状态,count=0这个上下文关系被存到寄存器中.
  2. 然后到第6步,这个时候thead 2开始执行,然后就变成了count = 1,返回给count,这个时候count=1.
  3. 然后再回到thead 1,这个时候由于上下文关系,thead 1拿到的寄存器中的count = 0,经过计算,得到count = 1,经过第13步的操作就覆盖了原来的count = 1的值,所以这个时候count依然是count = 1,所以这个数据并没有保护起来.

2.2、添加线程锁

说明:通过上面的图我们知道,结果依然是不准确的。所以我还要加一把锁,这个是用户级别的锁。

import threading,time

def run(n):
    lock.acquire()  #获取一把锁
    global num
    time.sleep(0.1)
    num += 1
    lock.release()  #释放锁

num = 0
lock = threading.Lock()  #添加一个锁的实例
t_obj = list()
for i in range(100):
    t = threading.Thread(target=run,args=("t-{0}".format(i),))
    t.start()
    t_obj.append(t)

for t in t_obj:
    t.join()    #因为join是等子线程执行的结果,如果不加,下面可能没有执行完就获取到num的值了


print("--------all thead has finished")
print("num:",num)  #获取num的结果

#输出
--------all thead has finished
('num:', 100)

 小结:

  1. 用theading.Lock()创建一个lock的实例。
  2. 在线程启动之前通过lock.acquire()加加锁,在线程结束之后通过lock.release()释放锁。
  3. 这层锁是用户开的锁,就是我们用户程序的锁。跟我们这个GIL没有关系,但是它把这个数据相当于copy了两份,所以在这里加锁,以确保同一时间只有一个线程,真真正正的修改这个数据,所以这里的锁跟GIL没有关系,你理解就是自己的锁。
  4. 加锁,说明此时我来去修改这个数据,其他人都不能动。然后修改完了,要把这把锁释放。这样的话就把程序编程串行了。

三、使用场景

  在用户层面加锁,使程序变成串行了,那我们在什么情况下用呢?

  1、我们在程序中间不能有sleep,因为程序变成串行,这样你再sleep,程序执行的时间就会变长。

  2、我们使用的时候确保数据量不是特别大,如果数据量大,也会影响我们的执行效率。

  3、如果你程序结束时,不释放锁的话,而且程序又是串行的,则就是占着坑,那永远在那边等着,所以最后需要释放锁。

转载于:https://www.cnblogs.com/zhangqigao/articles/7260398.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值