thread 模块

让我们先来看看thread 模块提供了什么。除了派生线程外,thread 模块还提供了基本的
同步数据结构,称为锁对象(lock object,也叫原语锁、简单锁、互斥锁、互斥和二进制信号
量)。如前所述,这个同步原语和线程管理是密切相关的。

表4-1 列出了一些常用的线程函数,以及LockType 锁对象的方法。

thread 模块的核心函数是start_new_thread()。它的参数包括函数(对象)、函数的参数以
及可选的关键字参数。将专门派生新的线程来调用这个函数。

把多线程整合进onethr.py 这个例子中。把对loop*()函数的调用稍微改变一下,得到示例
4-2 中的mtsleepA.py 文件。

thread 模块和锁对象

import thread
from concurrent.futures import thread
from time import sleep,ctime

def loop0():
    print('start loop 0 at:',ctime())
    sleep(4)
    print('loop 0 done at:',ctime())

def loop1():
    print('start loop 1 at:',ctime())
    sleep(2)
    print('loop1 done at:',ctime())

def main():
    print('starting at:',ctime())
    thread.start_new_thread(loop0,())
    thread.start_new_thread(loop1,())
    sleep(6)
    print('all DONE at:',ctime())

if __name__ == '__main__':
    main()

start_new_thread()必须包含开始的两个参数,于是即使要执行的函数不需要参数,也需
要传递一个空元组。

与之前的代码相比,本程序执行后的输出结果有很大不同。原来需要运行6~7 秒的时间,
而现在的脚本只需要运行4 秒,也就是最长的循环加上其他所有开销的时间之和。

$ mtsleepA.py
starting at: Sun Aug 13 05:04:50 2006
start loop 0 at: Sun Aug 13 05:04:50 2006
start loop 1 at: Sun Aug 13 05:04:50 2006
loop 1 done at: Sun Aug 13 05:04:52 2006
loop 0 done at: Sun Aug 13 05:04:54 2006
al l DONE at: Sun Aug 13 05:04:56 2006

睡眠4 秒和睡眠2 秒的代码片段是并发执行的,这样有助于减少整体的运行时间。你甚
至可以看到loop 1 是如何在loop 0 之前结束的。

这个应用程序中剩下的一个主要区别是增加了一个sleep(6)调用。为什么必须要这样做
呢?这是因为如果我们没有阻止主线程继续执行,它将会继续执行下一条语句,显示“all
done”然后退出,而loop0()和loop1()这两个线程将直接终止。

我们没有写让主线程等待子线程全部完成后再继续的代码,即我们所说的线程需要某种
形式的同步。在这个例子中,调用sleep()来作为同步机制。将其值设定为6 秒是因为我们知
道所有线程(用时4 秒和2 秒的)会在主线程计时到6 秒之前完成。

你可能会想到,肯定会有比在主线程中额外延时6 秒更好的线程管理方式。由于这个延
时,整个程序的运行时间并没有比单线程的版本更快。像这样使用sleep()来进行线程同步是
不可靠的。如果循环有独立且不同的执行时间要怎么办呢?我们可能会过早或过晚退出主线
程。这就是引出锁的原因。

再一次修改代码,引入锁,并去除单独的循环函数,修改后的代码为mtsleepB.py,如示
例4-3 所示。我们可以看到输出结果与mtsleepA.py 相似。唯一的区别是我们不需要再像
mtsleepA.py 那样等待额外的时间后才能结束。通过使用锁,我们可以在所有线程全部完成执
行后立即退出。其输出结果如下所示。

$ mtsleepB.py
starting at: Sun Aug 13 16:34:41 2006
start loop 0 at: Sun Aug 13 16:34:41 2006
start loop 1 at: Sun Aug 13 16:34:41 2006
loop 1 done at: Sun Aug 13 16:34:43 2006
loop 0 done at: Sun Aug 13 16:34:45 2006
al l DONE at: Sun Aug 13 16:34:45 2006

那么我们是如何使用锁来完成任务的呢?下面详细分析源代码。

import thread
from time import sleep,ctime

loops=[4,2]

def loop(nloop,nsec,lock):
    print('start loop',nloop,'at:',ctime())
    sleep(nsec)
    print('loop',nloop,'done at:',ctime())
    lock.release()

def main():
    print('starting at:',ctime())
    locks=[]
    nloops=range(len(loops))

    for i in nloops:
        lock=thread.allocate_lock()
        lock.aquire()
        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 DONE at:',ctime())

if __name__ == '__main__':
    main()
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐