lock 机制不管你是java, C#, 还是python都是常用的线程同步机制, 相比较C# 的锁机制, python的加锁显得比较简单, 直接调用threading 标准库的lock 就可以了. python 的 lock类有两个函数, 分别是acquire 函数以及 release 函数, 前者起到锁定的作用, 将状态设置为锁定状态, 后者则是解锁, 将状态设置为未锁定状态. 我们看看代码:
# python 多线程同步 lock import threading from time import sleep num = 0 lock = threading.Lock() def func(st): global num print(threading.currentThread().getName() + ‘ try to acquire the lock‘) if lock.acquire(): # 将状态修改为locked print(threading.currentThread().getName() + ‘ acquire the lock.‘) print(threading.currentThread().getName() + " :%s" % str(num)) num += 1 # sleep(st) print(threading.currentThread().getName() + ‘ release the lock.‘) lock.release() # 将状态修改为unlocked t1 = threading.Thread(target=func, args=(8,)) t2 = threading.Thread(target=func, args=(4,)) t3 = threading.Thread(target=func, args=(2,)) t1.start() t2.start() t3.start()
我们开了三个线程去调用同一个func 函数, 由于线程的不确定性, 如果没有加锁, 此时运行的话就会很混乱, 三个线程去执行同一个函数, 如果涉及到了变量的数据变化更是坑!因此我们加了锁, 确保数据的正确性, 函数执行的顺序性!
semaphore 信号量机制在python 里面也很简单就能够实现线程的同步。如果对操作系统有一定的了解, 那么对操作系统的PV原语操作应该有印象, 信号量其实就是基于这个机制的.semaphore 类是threading 模块下的一个类, 主要两个函数: acquire 函数, release 函数这和lock 类的函数是一样的, 只不过功能不一样, semaphore 机制的acquire 函数的参数允许你自己设置最大的并发量, 就是说允许多少个线程来操作同一个函数或是变量, 同时执行一次就会递减一次, release 函数则是递增, 如果计数到了0, 则阻塞起线程, 不再允许线程访问该方法或是变量.
# python 多线程同步 semaphore import threading # 初始化信号量数量...当调用acquire 将数量置为 0, 将阻塞线程等待其他线程调用release() 函数 semaphore = threading.Semaphore(2) def func(): if semaphore.acquire(): for i in range(5): print(threading.currentThread().getName() + ‘ get semaphore‘) semaphore.release() print(threading.currentThread().getName() + ‘ release semaphore‘) if __name__ == ‘__main__‘: for i in range(4): t1 = threading.Thread(target=func) t1.start()
我们一次允许两个线程同时执行函数, 这可以从截图看出来:
event 机制不仅能够实现线程间的通信, 也是实现线程同步的一个好方法。事件是线程之间通信的最简单的机制之一, 一个线程指示一个事件和其他线程等待它.
event.py 是threading 模块下的一个类, 相比较前面两个机制, 这个类提供了四个方法, 分别是 is_set() 函数, set() 函数, clear() 函数, wait() 函数.
is_set判断事件管理标志是不是为true, 只有为true时, 才会返回
set 将标志设置为true
clear 将标志设置为flase
wait 等到标志为true时, 才会停止阻塞线程
import logging import threading import time # 打印线程名以及日志信息 logging.basicConfig(level=logging.DEBUG, format="(%(threadName)-10s : %(message)s", ) def wait_for_event_timeout(e, t): """Wait t seconds and then timeout""" while not e.isSet(): logging.debug("wait_for_event_timeout starting") event_is_set = e.wait(t) # 阻塞, 等待设置为true logging.debug("event set: %s" % event_is_set) if event_is_set: logging.debug("processing event") else: logging.debug("doing other work") e = threading.Event() # 初始化为false t2 = threading.Thread(name="nonblock", target=wait_for_event_timeout, args=(e, 2)) t2.start() logging.debug("Waiting before calling Event.set()") # time.sleep(7) e.set() # 唤醒线程, 同时将event 设置为true logging.debug("Event is set")