Python中多线程编程:创建100个实例并各自启动线程,如何确保线程安全?
Python3 小白问题求解。。这个想法对否? Class 下面有一些成员变量,和一些函数,以及一些函数下的局部变量。
Python中多线程编程:创建100个实例并各自启动线程,如何确保线程安全?
关键在于单个实例中的多个线程是否共享了内存(数据),和你创建的实例的个数没啥关系
核心就是加锁。在Python里,用 threading.Lock() 创建一个锁对象,所有线程在访问共享资源前必须先获取这个锁。
下面是一个例子,创建100个线程,每个线程安全地递增一个共享计数器:
import threading
class SharedCounter:
def __init__(self):
self.value = 0
self.lock = threading.Lock() # 创建锁
def increment(self):
with self.lock: # 使用with语句自动获取和释放锁
self.value += 1
print(f"线程 {threading.current_thread().name} 增加计数到: {self.value}")
def worker(counter):
counter.increment()
if __name__ == "__main__":
counter = SharedCounter()
threads = []
# 创建并启动100个线程
for i in range(100):
t = threading.Thread(target=worker, args=(counter,), name=f"Thread-{i}")
threads.append(t)
t.start()
# 等待所有线程完成
for t in threads:
t.join()
print(f"\n最终计数器的值是: {counter.value}")
代码解释:
SharedCounter类封装了共享的value和一把锁lock。increment方法使用with self.lock:上下文管理器。这确保了同一时间只有一个线程能执行self.value += 1这行代码,避免了竞争条件。- 主程序创建了一个
SharedCounter实例和100个线程,每个线程都调用increment方法。 - 最终输出会是100,证明所有增加操作都安全完成了。
关键点:
- 锁(Lock):是确保线程安全最基本和常用的工具。
with语句能帮你自动处理锁的获取和释放,即使代码发生异常也能正确释放锁,这是最佳实践。 - 共享状态:识别出哪些数据(比如例子里的
counter.value)是被多个线程共享的,然后只在这些关键区域加锁。 - GIL(全局解释器锁):在Python中,GIL确保同一时刻只有一个线程执行Python字节码。但这不意味着你可以忽略线程安全!对于像
+=这样的操作,它包含“读取-修改-写入”多个步骤,如果没有锁,多个线程交叉执行这些步骤仍然会导致数据错误。GIL保护的是解释器内部状态,而不是你的业务逻辑。
一句话总结:用 threading.Lock 保护所有对共享变量的修改。
成员变量都没事,不是全局或者类变量就行
然而里面还有其他子对象,可能有共享的引用
你需要搞清你使用的其他对象是不是线程安全的
你得先搞清楚啥叫线程安全
py 有 gil 多线程需要考虑的已经少很多了
python3 還是有 GIL(Global Interpreter Lock),multi-threading 效率很差,但至少 thread safety
有 gil 还考虑啥线程安全啊
自己不确定的东西别乱说啊, 别误导别人.
愿闻其详
线程不安全是因为两个以上的线程,同时对一块共享内存做修改。
GIL 只是保证了原子性,并不能完全保证线程安全
系统随时可能中断线程 a 的执行,切换到线程 b 上。gil 保证的不是一个线程执行完再执行下一个线程。看这个有个小实验: https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/00143192823818768cd506abbc94eb5916192364506fa5d000
学习了
线程安全和原子性之间并不是划等号的,很多函数内部调用会有多个步骤,这些步骤被打断一样会产生线程不安全,而原子性只是保证单个变量的操作是一次性完成的,并不能保证一串操作不被打断
对啊,所以 gil 并不能保证原子性啊
嗯我理解错了, 不过我这里回答的是你 10 楼的问题


