python中的线程锁

时间:2023-03-09 19:15:02
python中的线程锁

锁对象

原始锁是一个在锁定时不属于特定线程的同步基元组件。在Python中,它是能用的最低级的同步基元组件,由 _thread 扩展模块直接实现。

原始锁处于 "锁定" 或者 "非锁定" 两种状态之一。它被创建时为非锁定状态。它有两个基本方法, acquire() 和 release() 。当状态为非锁定时, acquire() 将状态改为 锁定 并立即返回。当状态是锁定时, acquire() 将阻塞至其他线程调用 release() 将其改为非锁定状态,然后 acquire() 调用重置其为锁定状态并返回。 release() 只在锁定状态下调用; 它将状态改为非锁定并立即返回。如果尝试释放一个非锁定的锁,则会引发 RuntimeError  异常。

锁同样支持 上下文管理协议。

当多个线程在 acquire() 等待状态转变为未锁定被阻塞,然后 release() 重置状态为未锁定时,只有一个线程能继续执行;至于哪个等待线程继续执行没有定义,并且会根据实现而不同。

acquire(blocking=Truetimeout=-1)可以阻塞或非阻塞地获得锁。当调用时参数 blocking 设置为 True (缺省值),阻塞直到锁被释放,然后将锁锁定并返回 True 。在参数 blocking 被设置为 False 的情况下调用,将不会发生阻塞。如果调用时 blocking 设为 True 会阻塞,并立即返回 False ;否则,将锁锁定并返回 True。当浮点型 timeout 参数被设置为正值调用时,只要无法获得锁,将最多阻塞 timeout 设定的秒数。timeout 参数被设置为 -1 时将无限等待。当 blocking 为 false 时,timeout 指定的值将被忽略。如果成功获得锁,则返回 True,否则返回 False (例如发生 超时 的时候)。

在 3.2 版更改: 新的 timeout 形参。
在 3.2 版更改: 现在如果底层线程实现支持,则可以通过POSIX上的信号中断锁的获取。

release()

释放一个锁。这个方法可以在任何线程中调用,不单指获得锁的线程。当锁被锁定,将它重置为未锁定,并返回。如果其他线程正在等待这个锁解锁而被阻塞,只允许其中一个允许。在未锁定的锁调用时,会引发 RuntimeError 异常。没有返回值。

递归锁对象

重入锁是一个可以被同一个线程多次获取的同步基元组件。在内部,它在基元锁的锁定/非锁定状态上附加了 "所属线程" 和 "递归等级" 的概念。在锁定状态下,某些线程拥有锁 ; 在非锁定状态下, 没有线程拥有它。

若要锁定锁,线程调用其 acquire() 方法;一旦线程拥有了锁,方法将返回。若要解锁,线程调用 release() 方法。 acquire()/release() 对可以嵌套;只有最终 release() (最外面一对的 release() ) 将锁解开,才能让其他线程继续处理 acquire() 阻塞。

递归锁也支持 上下文管理协议。

条件对象

条件变量总是与某种类型的锁对象相关联,锁对象可以通过传入获得,或者在缺省的情况下自动创建。当多个条件变量需要共享同一个锁时,传入一个锁很有用。锁是条件对象的一部分,你不必单独地跟踪它。

条件变量服从 上下文管理协议:使用 with 语句会在它包围的代码块内获取关联的锁。 acquire() 和 release() 方法也能调用关联锁的相关方法。

其它方法必须在持有关联的锁的情况下调用。 wait() 方法释放锁,然后阻塞直到其它线程调用 notify() 方法或 notify_all() 方法唤醒它。一旦被唤醒, wait() 方法重新获取锁并返回。它也可以指定超时时间。

The notify() method wakes up one of the threads waiting for the condition variable, if any are waiting. The notify_all() method wakes up all threads waiting for the condition variable.

注意: notify() 方法和 notify_all() 方法并不会释放锁,这意味着被唤醒的线程不会立即从它们的 wait() 方法调用中返回,而是会在调用了 notify() 方法或 notify_all() 方法的线程最终放弃了锁的所有权后返回。

使用条件变量的典型编程风格是将锁用于同步某些共享状态的权限,那些对状态的某些特定改变感兴趣的线程,它们重复调用 wait() 方法,直到看到所期望的改变发生;而对于修改状态的线程,它们将当前状态改变为可能是等待者所期待的新状态后,调用 notify() 方法或者 notify_all() 方法。

信号量对象

这是计算机科学史上最古老的同步原语之一,早期的荷兰科学家 Edsger W. Dijkstra 发明了它。(他使用名称 P() 和 V() 而不是 acquire() 和 release() )。

一个信号量管理一个内部计数器,该计数器因 acquire() 方法的调用而递减,因 release() 方法的调用而递增。 计数器的值永远不会小于零;当 acquire() 方法发现计数器为零时,将会阻塞,直到其它线程调用 release() 方法。

事件对象

这是线程之间通信的最简单机制之一:一个线程发出事件信号,而其他线程等待该信号。

一个事件对象管理一个内部标志,调用 set() 方法可将其设置为true,调用 clear() 方法可将其设置为false,调用 wait() 方法将进入阻塞直到标志为true。

 

定时器对象

此类表示一个操作应该在等待一定的时间之后运行 --- 相当于一个定时器。 Timer 类是 Thread 类的子类,因此可以像一个自定义线程一样工作。

与线程一样,通过调用 start() 方法启动定时器。而 cancel() 方法可以停止计时器(在计时结束前), 定时器在执行其操作之前等待的时间间隔可能与用户指定的时间间隔不完全相同。

栅栏对象

3.2 新版功能.

栅栏类提供一个简单的同步原语,用于应对固定数量的线程需要彼此相互等待的情况。线程调用 wait() 方法后将阻塞,直到所有线程都调用了 wait() 方法。此时所有线程将被同时释放。

栅栏对象可以被多次使用,但进程的数量不能改变。

from threading import Thread,Lock
import random
# 锁的作用
# arr = []
#
# #原始锁处于 "锁定" 或者 "非锁定" 两种状态之一。它被创建时为非锁定状态。它有两个基本方法, acquire() 和 release() 。当状态为非锁定时, acquire() 将状态改为 锁定 并立即返回。当状态是锁定时, acquire() 将阻塞至其他线程调用 release() 将其改为非锁定状态,然后 acquire() 调用重置其为锁定状态并返回。 release() 只在锁定状态下调用; 它将状态改为非锁定并立即返回。如果尝试释放一个非锁定的锁,则会引发 RuntimeError 异常。
#
# l = Lock() # 状态默认是非锁定的
# l.acquire()
#
# def test1():
# l.acquire() # 如果之前的锁是锁定的,再次上锁发生阻塞
# # 如果之前的锁是非锁定的,可以实现上锁
# arr.append(random.randint(1,10))
#
# t = Thread(target=test1)
# t.start()
#
#
#
# def test2():
# arr.append(random.randint(1,0)) """
爬取豆瓣电影top250
"""
# import requests
# import lxml.etree as etree
# urls = ["https://movie.douban.com/top250?start=%s"%i for i in range(0,226,25)]
#
# class Mytest(Thread):
# def __init__(self):
# super(Mytest,self).__init__()
# def run(self):
# while len(urls)>0:
# url = urls.pop()
# res = requests.get(url).text
# html = etree.HTML(res)
# titles = html.xpath("//div[@class='hd']/a/span[1]/text()")
# print(self.name,titles)
#
# for i in range(2):
# mytest = Mytest()
# mytest.start() """
向列表中插入元素并打印列表
"""
# import time
# arr = []
# l = Lock()
# class Mytest(Thread):
# def __init__(self):
# super(Mytest,self).__init__()
# def run(self):
# time.sleep(1)
# l.acquire()
# arr.append(random.randint(0,10))
# print(arr)
# l.release()
#
# for i in range(20):
# t = Mytest()
# t.start() """
递归锁 复用锁 RLock()
在一个线程内可以重复上锁不被阻塞。
"""
from threading import RLock
import time
r = RLock()
l = Lock()
def test1():
r.acquire()
r.acquire()
print("")
time.sleep(2)
r.release()
r.release() def test2():
r.acquire()
print('test2') t = Thread(target=test1)
t.start() t2 = Thread(target=test2)
t2.start() # 死锁
# import time
# l1 = Lock()
# l2 = Lock()
# def fun1():
# l1.acquire()
# print("fun1 is running")
# time.sleep(1)
# l2.acquire() # 阻塞
# print("fun1 is end")
# l1.release()
# l2.acquire()
# def fun2():
# l2.acquire()
# print("fun2 is running")
# l1.acquire() # 阻塞
# print("fun2 is end")
# l2.release()
# l1.release()
#
# t1 = Thread(target=fun1)
# t1.start()
# t2 = Thread(target=fun2)
# t2.start() """
锁的上下文使用方法
向列表中插入元素并打印列表
"""
# import time,random
# arr = []
# l = Lock()
# def fun():
# time.sleep(1)
# with l: # 上锁 自动解锁的功能
# arr.append(random.randint(0,10))
# print(arr)
#
# for i in range(20):
# t = Thread(target=fun)
# t.start() """
条件锁
class Condition(lock=RLock) 默认是递归锁
方法:
acquire() 上锁
release() 释放锁
wait(timeout=) 等待(释放锁) 阻塞(等待被通知)
notify(n=) 通知 唤醒n个线程
wait_for(predicate,timeout=) predicate 是一个函数 返回 True False
notify_all() 唤醒所有线程
"""
# from threading import Condition
# import time
# con = Condition(lock=Lock())
#
# arr = []
#
# class XM(Thread):
# def __init__(self):
# super(XM,self).__init__()
# def run(self):
# with con:
# while True:
# time.sleep(1)
# arr.append(1)
# length = len(arr)
# print("小明添加了1个鱼丸,锅内还有%s个鱼丸"%length)
# if length>=5:
# con.notify_all()
# con.wait()
#
#
# class XH(Thread):
# def __init__(self,name):
# super(XH,self).__init__()
# self.name = name
# def run(self):
# with con:
# while True:
# time.sleep(1)
# arr.pop()
# length = len(arr)
# print("%s吃了1个鱼丸,锅内还有%s个鱼丸"%(self.name,length))
# if length<=0:
# con.notify()
# con.wait()
#
# xm = XM()
# xm.start()
#
# xh = XH("小红1")
# xh.start()
#
# xh1 = XH("小红2")
# xh1.start() """
wait_for(predicate,timeout=)
等待 当predicate 返回值为 False 阻塞 当返回值为True 运行
wait() 等待,阻塞 。直到被 notify
"""
# from threading import Condition
# import time
# con = Condition()
# con.acquire()
#
# def fun():
# time.sleep(5)
# return True
# con.wait_for(fun)
# print(123) """
Semaphore 信号量对象
信号量通常用于保护数量有限的资源,例如数据库服务器。在资源数量固定的任何情况下,都应该使用有界信号量。在生成任何工作线程前,应该在主线程中初始化信号量。
"""
# from threading import Semaphore
# import time
# b = Semaphore(value=3) # 技术面试 每次3个人 # class Ms(Thread):
# def __init__(self):
# super(Ms,self).__init__()
# def run(self):
# with b:
# print("<%s>开始面试,倒计时3秒钟"%self.name)
# time.sleep(3)
# print("<%s>面试结束,有请下一个同学"%self.name)
#
#
# for i in range(20):
# m = Ms()
# m.start() """
事件锁
is_set() 判断事件锁内部开关是否为true
set() 设置事件锁内部开关为 true
clear() 设置事件锁内部开关为 false
wait() 阻塞 等待事件锁内部开关为true 然后运行
"""
# from threading import Event
# import time
#
# e1 = Event()
# e1.set()
#
# e2 = Event()
#
# arr = []
# class XM(Thread):
# def __init__(self):
# super(XM,self).__init__()
# def run(self):
# while True:
# e1.wait()
# time.sleep(1)
# arr.append(1)
# length = len(arr)
# print("小明添加了1个鱼丸,锅内还有%s个鱼丸"%length)
# if length>=5:
# e1.clear()
# e2.set()
#
# class XH(Thread):
# def __init__(self):
# super(XH, self).__init__()
# def run(self):
# while True:
# e2.wait()
# time.sleep(1)
# arr.pop()
# length = len(arr)
# print("小红吃了1个鱼丸,锅内还剩%s个鱼丸"%length)
# if length<=0:
# e2.clear()
# e1.set()
#
# xm = XM()
# xm.start()
# xh = XH()
# xh.start() """
定时器对象
"""
# import time
# def fun():
# time.sleep(3)
# print("123")
#
# fun()
# print("开始") # from threading import Timer
# # Timer 是一个倒计时线程
# def fun():
# print("123")
#
# t = Timer(3,fun)
# t.start()
# print("开始") """
栅栏对象
wait()
"""
# from threading import Barrier,active_count
# import time
#
# b = Barrier(5)
# l = Lock()
#
# class Test(Thread):
# def __init__(self):
# super(Test,self).__init__()
# def run(self):
# b.wait()
# with l:
# print("%s 开始运行"%self.name)
#
# for i in range(20):
# time.sleep(2)
# t = Test()
# with l:
# print("创建了%s线程"%t.name)
# t.start()