10.4.1 Lock
Lock是Python里最简单的同步原语,由_thread扩展模块直接实现。一旦一个线程获得一个锁,随后获取它的尝试将被阻塞,直到它被释放。Lock是比较低级的同步原语,当被锁定以后不属于特定的线程。任何线程都可以释放它。原始锁有两个状态:锁定(locked)和解锁(unlocked)。刚创建时的状态是unlocked。
Lock有两个方法:acquire()和release()。
acquire()方法建立一个锁,建立成功返回True,建立失败返回False。基本格式是:
其中timeout参数指定加锁多少秒。
release()方法释放锁,用来将locked状态改为unlocked状态并立即返回。如果acquire()加锁失败,则阻塞,表明其他线程已经加锁。只有在状态是locked时调用release()方法得到True,如果是unlocked状态,调用release()方法会抛出RunTimeError异常。
这两个方法必须是成对出现的,必须release()后才能调用acquire(),否则会造成死锁。
需要注意的是,Python有一个GIL(Global Interpreter Lock)机制,任何线程在运行之前必须获取这个全局锁才能执行,每执行完100字节码后,全局锁才会释放,切换到其他线程执行。
例如,用多个线程对x值进行修改时,要保证值不会混乱,采用Lock机制,代码如下:
运行结果如下:
如果把lock.acquire()、lock.release()这两句删掉再运行,会发现多个线程之间没有规律,输出结果杂乱无章,如图10-4所示。
图10-4 没有同步的输出
从代码中可以看出使用Lock类实现数据同步,需经过3个步骤。
①创建锁对象:lock=threading.Lock()。
②锁定资源:lock.acquire()。
③释放资源:lock.release()。
【案例10-5】 多线程实现窗口卖票。
运行结果: