引言
上节我们演示了使用线程和不使用线程所造成的不同结果,里面提到一个sleep(6)的事情。忘记的请返回去看看。那为什么我们必须要加一个sleep(6)呢?这是因为如果我们没有阻止主线程继续执行,它将会继续执行下一条语句,然后退出。这样就会导致我们的其它线程得不到执行就已经结束了。
在上节的例子当中,我们并没有写让主线程去等待子线程全部完成后再继续的代码,即我们所说的线程需要某种意义上的同步,在这个例子中我们只是通过sleep(6)实现了另一种形式的伪同步机制。
这个时候你肯定会想,那是不是会有比在主线程中延迟6秒更好的线程管理方式呢?好,这个时候就来引入我们的锁的概念了。
锁
直接看例子,在上节的代码中我们引入锁,替代掉sleep(6)。
from time import sleep,ctime
import _thread
loops = [4,2]
def loop(nloop,nesc,lock):
print("start loop",nloop,"at:",ctime())
sleep(nesc)
print("loop",nloop,"end at:",ctime())
lock.release() #释放锁
def main():
print("start at:",ctime())
locks = []
nloops = range(len(loops))
for i in nloops:
lock = _thread.allocate_lock()
lock.acquire()
locks.append(lock)#锁的创建
for i in nloops:
_thread.start_new_thread(loop,(i,loops[i],locks[i])) # 启动的时候给他一个锁
for i in nloops:
while locks[i].locked():pass # 都释放了锁之后继续
print("all end at:",ctime())
if __name__ == "__main__":
main()
运行后>>>
start at: Sun Sep 9 23:35:59 2018
start loop 1 at: Sun Sep 9 23:35:59 2018
start loop 0 at: Sun Sep 9 23:35:59 2018
loop 1 end at: Sun Sep 9 23:36:01 2018
loop 0 end at: Sun Sep 9 23:36:03 2018
all end at: Sun Sep 9 23:36:03 2018
看起来和我们上节课中的sleep(6)输出是一致的,但是他们本质上是有差别的。在这个例子中我们给每个子线程都分配了一个锁,当它执行结束后,又把该锁给释放掉。
我们来仔细分析一下,在main()中我们首先创建了一个锁的列表,通过使用allocate_lock()函数得到锁的对象,然后通过acquire()方法取得每个锁,说白了就是“把锁锁上”,一旦锁被锁上,就可以把它添加到锁列表中去。然后在后面启动子线程的时候给子线程送过去。这就是我们这节课的核心思想了。
有的同学会有疑问,在上面的例子中为什么不在循环创建锁的时候就顺便创建子线程呢?问的好,原因有二:1、因为我们想要同步线程,所有这里我们不做限制。2、获取锁需要时间,如果线程执行太快会导致在获取到锁之前线程就已经结束了。
更多内容请关注公众号“计算机自学平台