【Python】线程间同步的实现与代码

262 阅读2分钟

「这是我参与2022首次更文挑战的第7天,活动详情查看:2022首次更文挑战」。

线程间同步

常用线程间同步方法有:

  1. 临界区:通过对多线程的串行化来访问公共资源或一段代码
    • 速度快,适合控制数据访问
    • 在任意时刻只允许一个线程访问共享资源,当有一个线程进入后,其他试图访问共享资源的线程将会被挂起,并一直等到进入临界区的线程离开,临界在被释放后,其他线程才可以抢占
  2. 互斥量:为协调对一个共享资源的单独访问而设计
    • 只有拥有互斥量的线程,才有权限去访问系统的公共资源
    • 互斥量只有一个,所以能够保证资源不会同时被多个线程访问
    • 互斥不仅能实现同一应用程序的公共资源安全共享,还能实现不同应用程序的公共资源安全共享
  3. 信号量:为控制一个具有有限数量的用户资源而设计
    • 它允许多个线程在同一个时刻去访问同一个资源,但一般需要限制同一时刻访问此资源的最大线程数目
  4. 事件:用来通知线程有一些事件已发生,从而启动后继任务的开始

Python 中的互斥锁

互斥量又称为互斥锁,Python 的 threading 模块中定义了 Lock 类,可以方便的处理锁定。一旦某个线程获得了这个锁,其他的线程再要想获得这个锁就必须阻塞,直到锁被释放。

  • 创建一个锁: mutex = threading.Lock()
  • 获得一个锁: mutex.acquire([blocking=True, timeout=-1])
    • 成功获得锁时返回 true
    • blockingture 时,以阻塞方式获得锁,即要等到锁释放后方能加锁
    • timeout 为超时时间,如果设定了 timeout,则在超时后通过返回值可以判断是否得到了锁,从而可以进行一些其他的处理
  • 释放一个锁: mutex.release()

举个例子

import threading

num = 0
mutex = threading.Lock()  # 创建一个锁

def add():
    """ num 数值每次加 1,执行 1000000 次"""
    global num
    for _ in range(1000000):
        if mutex.acquire():
            num += 1
            mutex.release()
            
t1 = threading.Thread(target= add)
t2 = threading.Thread(target= add)

# 开始线程
t1.start()
t2.start()

# 等待线程结束
t1.join()
t2.join()

print(num)

运行代码,可以看到输出 2000000 ,如果去掉代码中的加锁和释放锁的语句,则会得到不符合预期的结果。