Python在多线程环境中如何保护共享资源

文摘   科技   2024-08-05 19:43   广东  

ISEE小语


“人生是一场漫长的修行,我们这一生都在渡,渡人,渡心,渡自己。想要过一个舒服的人生,最重要的是满足自己,而不是讨好别人。”

——《断舍离》




【引言】

本次介绍Python在多线程环境中如何保护共享资源。

使用threading.Lock是Python标准库中的一个同步原语,在某些情况下,多个线程可能会同时访问共享数据,这可能导致数据不一致。使用 threading.Lock进行线程同步,可以保证同一时间只有一个线程可以访问共享资源。


【环境】

Python
3.9.16



接下来我们看一下不使用锁使用锁的区别


我们来实现一个实例:

    实现了一个简单的多线程计数器功能

    首先,定义了一个名为workers的函数。这个函数在被线程执行时,会将counter的值增加100000次

    然后,创建一个空列表threads用于存储线程对象。使用一个循环创建10个线程,每个线程都执行 workers函数

    最后,执行并等所有线程执行完毕。看最终结果。


【不使用锁】

以下实例,为了效果明显,将实例循环了打印10次

# -*- coding: utf-8 -*-# 不使用锁
import threading
for i in range(10): counter = 0
def workers(): """线程工作函数""" global counter for _ in range(100000): counter += 1 # 这里没有使用锁
threads = [] for _ in range(10): thread = threading.Thread(target=workers) threads.append(thread) thread.start()
for thread in threads: thread.join()
print(f"不使用锁,第{i}次计数器值: {counter}")

(左右滑动查看完整代码)

结果:

分析:

预期逻辑结果

    每个线程都应该将 counter 增加 100000 次,总共有 10 个线程。因此,理论上,最终的 counter 值应该是 10 * 100000 = 1000000。

实际结果的不确定性:

    由于没有使用锁,多个线程可能会同时读取和修改 counter 的值。这可能导致数据竞争(race condition),使得某些增量操作被覆盖或丢失。因此,实际输出结果可能不同于理论值,可能会是小于 1000000 的任何值,且在不同的运行中结果可能会有所不同。


【使用锁】

同上,为了效果明显,将实例循环了打印10次

# -*- coding: utf-8 -*-"""使用锁"""import threading
for i in range(10): counter = 0 lock = threading.Lock() # 创建一个锁
def workers(): """线程工作函数""" global counter for _ in range(100000): with lock: # 使用锁保护对 counter 的访问 counter += 1
threads = [] for _ in range(10): thread = threading.Thread(target=workers) threads.append(thread) thread.start()
for thread in threads: thread.join()
print(f"使用锁,第{i}次计数器值: {counter}")

(左右滑动查看完整代码)

结果:

分析:

    当使用锁时,最终输出应该始终是 1000000,因为锁确保了每次只有一个线程可以修改 counter,从而避免了数据竞争。


【总结】

    threading.Lock 是多线程编程中一个非常重要的工具,用于确保数据的一致性和安全性。它适用于需要保护共享资源的各种场景,尤其是涉及到写入操作的情况。通过使用 Lock,可以有效防止数据竞争和潜在的错误,确保程序的稳定性和可靠性。

    使用场景:

    1、共享资源的修改,当多个线程需要同时修改一个共享变量(如计数器、列表等)时,使用 Lock 可以确保每次只有一个线程能够进行修改。

    2、保护临界区,当一段代码必须在一段时间内保持一致性(例如,读取及写入数据库、文件等),这段代码被称为临界区。使用 Lock 可以保证任何时刻只有一个线程能执行这段代码。

    3、降低死锁发生的可能性,当多个线程需要获取多个锁时,可能会导致死锁。虽然 Lock 本身不能预防死锁,但在适当的设计中使用锁,可以降低死锁发生的可能性。

    4、限流和控制访问,在一些情况下,需要控制对某个资源的访问频率。例如,限制同一时间内对文件的访问数量,可以使用锁来管理。


个人观点,在使用中请根据实际项目进行使用。



点个“”和“在看”,是对小栈最大的支持!

     

文章就分享到这儿,喜欢就点个吧!




推荐阅读  点击标题可跳转


ISEE小栈
没有花里胡哨,简单才是王道。
 最新文章