18 线程安全与锁的使用

在上篇中,我们讨论了使用 asyncio 模块进行并发编程的方法,虽然 asyncio 提供了一种有效的处理并发的方式,但在许多情况下,我们仍然需要使用传统的线程来实现并发。在这篇文章中,我们将深入探讨线程安全以及如何使用来确保多线程程序的正确性。

什么是线程安全

线程安全 是指在多线程环境下,多个线程并发访问共享数据时,程序的行为是正确且一致的。简单来说,就是当多个线程同时读取或修改共享数据时,不会导致数据的损坏或不一致。

例如,考虑以下代码片段:

1
2
3
4
5
6
counter = 0

def increment():
global counter
for _ in range(100000):
counter += 1

在没有处理线程安全的情况下,如果多个线程同时执行increment函数,可能会导致 counter 的最终值不正确,因为 counter += 1 并不是一个原子操作,多个线程可能会并发执行这条语句而互相干扰。

使用锁来确保线程安全

锁的基础

为了解决上述问题,我们可以使用Lock)。Lock 是 Python threading 模块提供的一个简单的同步原语,它可以确保同一时间只有一个线程能够访问某段关键代码。

下面是一个使用 Lock 的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import threading

counter = 0
lock = threading.Lock()

def increment():
global counter
for _ in range(100000):
with lock: # 在这里获取锁
counter += 1 # 对共享资源的访问

# 创建多个线程
threads = []
for _ in range(10):
thread = threading.Thread(target=increment)
thread.start()
threads.append(thread)

# 等待所有线程完成
for thread in threads:
thread.join()

print(counter) # 输出最终的计数值

在上述代码中,关键部分是 with lock。这会在进入该块之前获得锁,并在退出该块时自动释放锁。这样可以确保 counter 的修改是线程安全的。

锁的使用注意事项

  1. 避免死锁:在同一线程内获取同一个锁可能会导致死锁,因此需要确保在适当的地方释放锁。
  2. 使用 with 语句:使用 with 语句可以简化锁的管理,确保锁在使用后正确释放。
  3. 锁的粒度:保持锁的粒度尽可能小。即在锁的控制下只执行必要的代码,可以减少锁的竞争,提高程序的并发性能。

更高级的锁:条件变量

有时,仅使用锁还不足以满足需求,可能需要使用更高级的同步机制,如条件变量(Condition)。条件变量可以使线程在某个条件不满足时等待,而不是一直持有锁。

下面是一个使用条件变量的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import threading

buffer = []
condition = threading.Condition()

def producer():
global buffer
for i in range(10):
with condition:
buffer.append(i)
print(f'Produced: {i}')
condition.notify() # 通知消费者

def consumer():
global buffer
while True:
with condition:
while not buffer: # 检查条件
condition.wait() # 等待
item = buffer.pop(0)
print(f'Consumed: {item}')

# 创建生产者和消费者线程
prod_thread = threading.Thread(target=producer)
cons_thread = threading.Thread(target=consumer)

prod_thread.start()
cons_thread.start()

prod_thread.join()
cons_thread.join()

在这个例子中,producerconsumer 通过 condition 进行同步,确保在缓冲区有数据可消费之前,消费者不会试图消费数据。

结论

在这一篇中,我们深入了解了线程安全的概念,以及如何使用条件变量来确保多线程编程中的数据一致性。掌握这些工具将帮助你在 Python 中编写更安全和高效的并发代码。

在接下来的文章中,我们将探讨模块与包管理的基础知识,包括模块的基础与导入规则,这将进一步提高你在 Python 编程中的能力。

18 线程安全与锁的使用

https://zglg.work/python-one/18/

作者

IT教程网(郭震)

发布于

2024-08-10

更新于

2024-08-10

许可协议

分享转发

交流

更多教程加公众号

更多教程加公众号

加入星球获取PDF

加入星球获取PDF

打卡评论