python3多线程-线程同步
1、介绍
多线程同时访问和操作同一资源,可能会造成数据错误。
- 脏读: 脏读最大的问题就是可能会读到不存在的数据。
- 幻读: 幻读侧重的方面是某一次的 select 操作得到的结果所表征的数据状态无法支撑后续的业务操作
- 不可重复读: 在一个事务内,最开始读到的数据和事务结束前的任意时刻读到的同一批数据出现不一致的情况。
解决办法是对资源进行限制,同一时间只允许一个线程进行访问和操作。
这里的资源,一般是指方法、函数或者代码块。即由多行代码组成,在逻辑上属于不可切分的操作。
2、线程同步
# 创建锁
t = threading.Lock()
# 上锁
t.acquire()
"""执行代码"""
# 释放锁
t.release()
- threading.Lock类可以创建锁对象,用来保护线程安全。
- 具体来说就是,锁对象的acquire方法和release方法可以分别进行上锁和释放锁的操作。
- 当代码执行过程中,遇到acquire方法时
- 如果此时锁是释放状态,那么就可以执行上锁操作,并且顺序执行,直到释放锁。
- 如果此时锁是上锁状态,那么就必须休眠,等待其它线程执行release方法释放锁。
- 综上,对于那些需要每次只允许一个线程操作的数据,可以将其操作放到 acquire 和 release 方法之间。
- 同一个锁对象可以在不同方法或函数中使用,会同步锁状态。另一方面,可以声明多个锁对象,互不干扰。
- 尤其需要注意,无论什么时候都存在释放锁的操作,而不是一直休眠中。需要尽量避免相邻代码连续调用acquire方法
3、Lock类
(1)类
class Lock:
def __init__(self) -> None:
(2)初始化
lock = threading.Lock()
(3)方法
# 上锁,对python系统资源独占,当该锁释放,其他线程才可以竞争
lock.acquire()
# 释放
lock.release()
# 判断是否上锁
def locked(self) -> bool
4、使用示例
如果不是考虑效率,而是为了并发的效果。一般,会搭配time.sleep函数,使每个线程在结束核心任务后,进行等待状态,让其他线程竞争执行。
如果存在多个线程使用同一资源,需要对各线程中涉及到该资源的代码进行上锁和释放处理,线程等待需要写在释放后才有预期的效果。
```python
import threading
import time
lock = threading.Lock()
def run():
arr = []
t = threading.Thread(target=a, args=["zs", 10, arr])
t1 = threading.Thread(target=p, args=[arr])
t1.start()
t.start()
t.join()
t1.join()
# 输出结果数组
def p(result: list):
while True:
lock.acquire()
print(len(result))
lock.release()
time.sleep(4)
def a(name, count, result: list):
for i in range(count):
lock.acquire()
print("正在添加", name + ":" + str(i))
result.append(name + ":" + str(i))
lock.release()
time.sleep(3)
run()
5、使用示例2
lock锁的使用:
线程内部,只有对共享的资源操作代码进行上锁和释放锁才有意义,对非共享的资源操作代码上锁是无意义的,反而降低了多线程的效率
不同线程间,只有使用的同一个锁对象,才会实现对某共享资源的同步操作。
各线程内执行到调用lock对象的acquire()方法时,判断锁是否被释放状态。如果是释放状态,则上锁,并继续执行。如果是锁定状态,则休眠至其他线程将锁打开。
import threading
import time
class stu:
# 同步有效,交替执行
lock = threading.Lock()
def __init__(self, name):
self.name = name
self.id = 0
# 同步无效,各自执行,混乱
# self.lock = threading.Lock()
def p(self, delay):
while self.id < 30:
self.lock.acquire()
print(self.name, self.id)
self.id = self.id + 1
time.sleep(delay)
self.lock.release()
stu1 = stu("a")
t1 = threading.Thread(target=stu1.p, args=[2.5])
t1.start()
stu2 = stu("b")
t2 = threading.Thread(target=stu2.p, args=[2.5])
t2.start()