Python中多线程编程:创建100个实例并各自启动线程,如何确保线程安全?

Python3 小白问题求解。。这个想法对否? Class 下面有一些成员变量,和一些函数,以及一些函数下的局部变量。


Python中多线程编程:创建100个实例并各自启动线程,如何确保线程安全?
18 回复

关键在于单个实例中的多个线程是否共享了内存(数据),和你创建的实例的个数没啥关系


核心就是加锁。在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}")

代码解释:

  1. SharedCounter 类封装了共享的 value 和一把锁 lock
  2. increment 方法使用 with self.lock: 上下文管理器。这确保了同一时间只有一个线程能执行 self.value += 1 这行代码,避免了竞争条件。
  3. 主程序创建了一个 SharedCounter 实例和100个线程,每个线程都调用 increment 方法。
  4. 最终输出会是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 不能保证正是因为操作不具有原子性。

线程安全和原子性之间并不是划等号的,很多函数内部调用会有多个步骤,这些步骤被打断一样会产生线程不安全,而原子性只是保证单个变量的操作是一次性完成的,并不能保证一串操作不被打断

对啊,所以 gil 并不能保证原子性啊

你自己 6 楼说的是有 gil 就不用考虑线程安全了嘛

嗯我理解错了, 不过我这里回答的是你 10 楼的问题

回到顶部