专栏首页橙子探索测试Python多线程之线程锁(Lock)和递归锁(RLock)实例

Python多线程之线程锁(Lock)和递归锁(RLock)实例

一、线程锁

Threading模块为我们提供了一个类,Threading.Lock锁。我们创建一个该类对象,在线程函数执行前,“抢占”该锁,执行完成后,“释放”该锁,则我们确保了每次只有一个线程占有该锁。这时候对一个公共的对象进行操作,则不会发生线程不安全的现象了。

1、我们先建立了一个threading.Lock类对象lock,在run方法里,我们使用lock.acquire()获得了这个锁。此时,其他的线程就无法再获得该锁了,他们就会阻塞在“if lock.acquire()”这里,直到锁被另一个线程释放:lock.release()。

2、如果多个线程要调用多个现象,而A线程调用A锁占用了A对象,B线程调用了B锁占用了B对象,A线程不能调用B对象,B线程不能调用A对象,于是一直等待。这就造成了线程“死锁”。

Threading模块中,也有一个类,RLock,称之为可重入锁。该锁对象内部维护着一个Lock和一个counter对象。counter对象记录了acquire的次数,使得资源可以被多次require。最后,当所有RLock被release后,其他线程才能获取资源。在同一个线程中,RLock.acquire可以被多次调用,利用该特性,可以解决部分死锁问题

3、当多个线程同时访问一个数据时,需加锁,排队变成单线程一个一个执行

4、加锁避免并发导致逻辑出错

5、每当一个线程a要访问共享数据时,必须先获得锁定;如果已经有别的线程b获得锁定了,那么就让线程a暂停,也就是同步阻塞;等到线程b访问完毕,释放锁以后,再让线程a继续

6、语法

lock=threading.Lock() #创建线程锁

lock = threading.RLock()#创建递归锁(多个锁时用这个)

lock.acquire() #锁住

lock.release() 释放锁

二、线程锁实例

#未加锁
如果多个线程同时操作某个数据,会出现不可预料的结果。比如以下场景:当小伙伴a在往火锅里面添加鱼丸的时候,小伙伴b在同时吃掉鱼丸,这很有可能导致刚下锅的鱼丸被夹出来了(没有熟),或者还没下锅,就去夹鱼丸(夹不到)
 
 
# coding=utf-8
import threading
import time
def chiHuoGuo(people, do):
    print("%s 吃火锅的小伙伴:%s" % (time.ctime(),people))
    time.sleep(1)
    for i in range(3):
        time.sleep(1)
        print("%s %s正在 %s 鱼丸"% (time.ctime(), people, do))
    print("%s 吃火锅的小伙伴:%s" % (time.ctime(),people))
class myThread (threading.Thread):   # 继承父类threading.Thread
    def __init__(self, people, name, do):
        '''重写threading.Thread初始化内容'''
        threading.Thread.__init__(self)
        self.threadName = name
        self.people = people
        self.do = do
    def run(self):   # 把要执行的代码写到run函数里面 线程在创建后会直接运行run函数
        '''重写run方法'''
        print("开始线程: " + self.threadName)
        chiHuoGuo(self.people, self.do)     # 执行任务
        print("结束线程: " + self.name)
print("yoyo请小伙伴开始吃火锅:!!!")
# 设置线程组
threads = []
# 创建新线程
thread1 = myThread("xiaoming", "Thread-1", "添加")
thread2 = myThread("xiaowang", "Thread-2", "吃掉")
# 添加到线程组
threads.append(thread1)
threads.append(thread2)
# 开启线程
for thread in threads:
    thread.start()
# 阻塞主线程,等子线程结束
for thread in threads:
    thread.join()
time.sleep(0.1)
print("退出主线程:吃火锅结束,结账走人")
 
 
C:\Users\wangli\PycharmProjects\AutoMation\venv\Scripts\python.exe C:/Users/wangli/PycharmProjects/AutoMation/case/test.py
yoyo请小伙伴开始吃火锅:!!!
开始线程: Thread-1
Fri Mar 15 08:33:19 2019 吃火锅的小伙伴:xiaoming
开始线程: Thread-2
Fri Mar 15 08:33:19 2019 吃火锅的小伙伴:xiaowang
Fri Mar 15 08:33:21 2019 xiaowang正在 吃掉 鱼丸
Fri Mar 15 08:33:21 2019 xiaoming正在 添加 鱼丸
Fri Mar 15 08:33:22 2019 xiaowang正在 吃掉 鱼丸
Fri Mar 15 08:33:22 2019 xiaoming正在 添加 鱼丸
Fri Mar 15 08:33:23 2019 xiaowang正在 吃掉 鱼丸
Fri Mar 15 08:33:23 2019 吃火锅的小伙伴:xiaowang
结束线程: Thread-2
Fri Mar 15 08:33:23 2019 xiaoming正在 添加 鱼丸
Fri Mar 15 08:33:23 2019 吃火锅的小伙伴:xiaoming
结束线程: Thread-1
退出主线程:吃火锅结束,结账走人
 
Process finished with exit code 0
#线程锁,单锁实例
import time,threading
def run(n):
    lock.acquire() #加锁
    global num
    num+=1
    lock.release() #释放锁
lock=threading.Lock()#获得线程锁
num=0
threads=[]
for i in range(50):
    thread=threading.Thread(target=run,args=("t-%s"%i,))
    thread.start()
    threads.append(thread)
for i in threads:
    i.join()
print("num:",num)
 
 
C:\Users\wangli\PycharmProjects\AutoMation\venv\Scripts\python.exe C:/Users/wangli/PycharmProjects/AutoMation/case/test.py
num: 50
 
Process finished with exit code 0
#线程锁,加锁实例
 
# coding=utf-8
import threading
import time
def chiHuoGuo(people, do):
    print("%s 吃火锅的小伙伴:%s" % (time.ctime(),people))
    time.sleep(1)
    for i in range(3):
        time.sleep(1)
        print("%s %s正在 %s 鱼丸"% (time.ctime(), people, do))
    print("%s 吃火锅的小伙伴:%s" % (time.ctime(),people))
class myThread (threading.Thread):   # 继承父类threading.Thread
    lock = threading.Lock()  # 线程锁
    def __init__(self, people, name, do):
        '''重写threading.Thread初始化内容'''
        threading.Thread.__init__(self)
        self.threadName = name
        self.people = people
        self.do = do
    def run(self):   # 把要执行的代码写到run函数里面 线程在创建后会直接运行run函数
        '''重写run方法'''
        print("开始线程: " + self.threadName)
        # 执行任务之前锁定线程
        self.lock.acquire()
        chiHuoGuo(self.people, self.do)     # 执行任务
        # 执行完之后,释放锁
        self.lock.release()
        print("结束线程: " + self.name)
print("yoyo请小伙伴开始吃火锅:!!!")
# 设置线程组
threads = []
# 创建新线程
thread1 = myThread("xiaoming", "Thread-1", "添加")
thread2 = myThread("xiaowang", "Thread-2", "吃掉")
# 添加到线程组
threads.append(thread1)
threads.append(thread2)
# 开启线程
for thread in threads:
    thread.start()
# 阻塞主线程,等子线程结束
for thread in threads:
    thread.join()
time.sleep(0.1)
print("退出主线程:吃火锅结束,结账走人")
 
 
C:\Users\wangli\PycharmProjects\AutoMation\venv\Scripts\python.exe C:/Users/wangli/PycharmProjects/AutoMation/case/test.py
yoyo请小伙伴开始吃火锅:!!!
开始线程: Thread-1
Fri Mar 15 08:36:39 2019 吃火锅的小伙伴:xiaoming
开始线程: Thread-2
Fri Mar 15 08:36:41 2019 xiaoming正在 添加 鱼丸
Fri Mar 15 08:36:42 2019 xiaoming正在 添加 鱼丸
Fri Mar 15 08:36:43 2019 xiaoming正在 添加 鱼丸
Fri Mar 15 08:36:43 2019 吃火锅的小伙伴:xiaoming
结束线程: Thread-1
Fri Mar 15 08:36:43 2019 吃火锅的小伙伴:xiaowang
Fri Mar 15 08:36:45 2019 xiaowang正在 吃掉 鱼丸
Fri Mar 15 08:36:47 2019 xiaowang正在 吃掉 鱼丸
Fri Mar 15 08:36:48 2019 xiaowang正在 吃掉 鱼丸
Fri Mar 15 08:36:48 2019 吃火锅的小伙伴:xiaowang
结束线程: Thread-2
退出主线程:吃火锅结束,结账走人
 
Process finished with exit code 0
#线程锁,多个锁时,需加递归锁
 
import threading, time
def run1():
    print("grab the first part data")
    lock.acquire()
    global num
    num += 1
    lock.release()
    return num
def run2():
    print("grab the second part data")
    lock.acquire()
    global num2
    num2 += 1
    lock.release()
    return num2
def run3():
    lock.acquire()
    res = run1()
    print('--------between run1 and run2-----')
    res2 = run2()
    lock.release()
    print(res, res2)
num, num2 = 0, 0
lock = threading.RLock() #递归锁
for i in range(3):
    t = threading.Thread(target=run3)
    t.start()
while threading.active_count() != 1:
    print(threading.active_count())
else:
    print('----all threads done---')
    print(num, num2)

本文分享自微信公众号 - 橙子探索测试(chengzitest),作者:王荔探索测试

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-12-31

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 【Jmeter篇】五种参数化方式之CSV Data Set Config参数化

    当线程设置线程数4个,会循环参数1 2 3 1;当循环设置4次,会循环参数1 4次;当线程设置2个,循环设置5次,会参数1和2分别循环5次

    橙子探索测试
  • Jmeter模拟真实用户压测场景之阶梯螺纹线程组、终极线程组、并发线程组实例

    2、搜索standard set并安装,重启jmeter,查看测试计划——添加线程组——jp@gc - Stepping Thread Group (depre...

    橙子探索测试
  • 进程与线程

      进程是表示资源分配的基本单位,又是调度运行的基本单位。例如,用户运行自己的程序,系统就创建一个进程,并为它分配资源,包括各种表格、内存空间、磁盘空间、I/O...

    橙子探索测试
  • 28.python 线程创建和传参

    在以前的文章中虽然我们没有介绍过线程这个概念,但是实际上前面所有代码都是线程,只不过是单线程,代码由上而下依次执行或者进入main函数执行,这样的单线程也称为主...

    猿说编程[Python和C]
  • python 线程创建和传参

    在以前的文章中虽然我们没有介绍过线程这个概念,但是实际上前面所有代码都是线程,只不过是单线程,代码由上而下依次执行或者进入main函数执行,这样的单线程也称为主...

    猿说编程[Python和C]
  • QListWidget添加删除

    qt和vc一样自由线程模式,线程间可以访问ui,但线程间访问qt内部封装问题,导致很多一样,可以使用信号和槽线程间传递数据,避免异常

    sofu456
  • 34.python 线程障碍对象Barrier

    action — 是一个可调用函数,当等待的线程到达了线程障碍数量parties,其中一个线程会首先调用action 对应函数,之后再执行线程自己内部的代码;

    猿说编程[Python和C]
  • java 线程总结(一)

    一个Java程序的运行不仅仅是main()方法的运行,而是main线程和多个其他线 程的同时运行。

    爱明依
  • 记一次Date引起的线上BUG处理

    在Java8以前,每每操控时间,我们经常使用的类库就是Date,并且会通过SimpleDateFormat类对时间进行格式化。你可知道?Date类是一个可变类,...

    程序员小明
  • python多线程开发

           通过threading.Thread创建一个线程对象,target是目标函数,name可以指定自己喜欢的名字,线程的启动需要借助start方法。线...

    py3study

扫码关注云+社区

领取腾讯云代金券