1.使用简单的共享全局数据 在父/主线程中探知线程何时结束 线程共享列表但是不共享列表里的对象 假设列表在内存中创建后不会移动
>>> import _thread as thread
>>> stdoutmutex=thread.allocate_lock()
>>> exitmutexes=[False]*10
>>> def counter(myId,count):
for i in range(count):
stdoutmutex.acquire()
print('[%s]=>%s'%(myId,i))
stdoutmutex.release()
exitmutexes[myId]=True #向主线程发送信号
>>> for i in range(10):
thread.start_new_thread(counter,(i,12))
运行结果 :
123145488994304[0]=>0
123145505783808[0]=>1
123145522573312[0]=>2
123145539362816[0]=>3
123145556152320[0]=>4
123145572941824[0]=>5
123145589731328[0]=>6
123145606520832[0]=>7
123145623310336[0]=>8
123145640099840[0]=>9
[0]=>10
[0]=>11
[2]=>0
[2]=>1
[2]=>2
[2]=>3
[2]=>4
[2]=>5
[2]=>6
[2]=>7
[2]=>8
[2]=>9
[2]=>10
[2]=>11
[1]=>0
[1]=>1
[1]=>2
[1]=>3
[1]=>4
[1]=>5
[1]=>6
[1]=>7
[1]=>8
[1]=>9
[1]=>10
[1]=>11
[7]=>0
[7]=>1
[7]=>2
[7]=>3
[7]=>4
[7]=>5
[7]=>6
[7]=>7
[7]=>8
[7]=>9
[7]=>10
[7]=>11
[6]=>0
[6]=>1
[6]=>2
[6]=>3
[6]=>4
[6]=>5
[6]=>6
[6]=>7
[6]=>8
[6]=>9
[6]=>10
[6]=>11
[8]=>0
[8]=>1
[8]=>2
[8]=>3
[8]=>4
[8]=>5
[8]=>6
[8]=>7
[8]=>8
[8]=>9
[8]=>10
[8]=>11
[4]=>0
[4]=>1
[4]=>2
[4]=>3
[4]=>4
[4]=>5
[4]=>6
[4]=>7
[4]=>8
[4]=>9
[4]=>10
[4]=>11
[3]=>0
[3]=>1
[3]=>2
[3]=>3
[3]=>4
[3]=>5
[3]=>6
[3]=>7
[3]=>8
[3]=>9
[3]=>10
[3]=>11
[5]=>0
[5]=>1
[5]=>2
[5]=>3
[5]=>4
[5]=>5
[5]=>6
[5]=>7
[5]=>8
[5]=>9
[5]=>10
[5]=>11
[9]=>0
[9]=>1
[9]=>2
[9]=>3
[9]=>4
[9]=>5
[9]=>6
[9]=>7
[9]=>8
[9]=>9
[9]=>10
[9]=>11
添加休眠时间:
>>> import _thread as thread,time
>>> stdoutmutex=thread.allocate_lock()
>>> numthreads=5
>>> exitmutexes=[thread.allocate_lock() for i in range(numthreads)]
>>> def counter(myId,count,mutex):
for i in range(count):
time.sleep(1/(myId+1))
with mutex:
print('[%s]=>%s'%(myId,i))
exitmutexes[myId].acquire()
>>> for i in range(numthreads):
thread.start_new_thread(counter,(i,5,stdoutmutex))
多了视觉延迟的效果.
2.threading模块
Python标准库带有两个线程模块:_thread,此前一直介绍的较低层面上的基本接口;threading,基于对象和类的较高层面的上的接口.threading模块在内部使用_thread模块来实现线程的对象以及常见同步化工具的功能.这个模块是松散地基于java语言中的threading模型中的一个子集,不过只有java程序员可以察觉到方式差异。
带有状态和run()行为的线程类实例,使用较高层面上的类java的threading模块对象连接方法在主线程中探知线程结束时间。
>>> import threading
>>> class Mythread(threading.Thread): #子类的Thread对象
def __init__(self,myId,count,mutex):
self.myId=myId
self.count=count #各个线程状态信息
self.mutex=mutex #共享对象,不是全局对象
threading.Thread.__init__(self)
def run(self): #run函数提供线程逻辑业务
for i in range(self.count): #同步化stdout访问
with self.mutex:
print('[%s]=>%s'%(self.myId,i))
>>> stdoutmutex=threading.Lock() #与thread.allocate_lock()相同
>>> threads=[]
>>> for i in range(10):
thread=Mythread(i,12,stdoutmutex) #创建开始10个线程
thread.start()
threads.append(thread)
线程在运行时间上是随机分布的.
接下来是设置生产者与消费者线程与共享队列进行通信:
>>> numconsumers=2
>>> numproduces=4
>>> nummessage=4
>>> import _thread as thread,queue,time
>>> safeprint=thread.allocate_lock()
>>> dataQueue=queue.Queue()
>>> def producer(idnum):
for msgnum in range(nummessages):
time.sleep(idnum)
dataQueue.put('[producer id=%d,count=%d]'%(idnum,msgnum))
>>> def consumer(idnum):
while True:
time.sleep(0.1)
try:
data=dataQueue.get(block=False)
except queue.Empty:
pass
else:
with safeprint:
print('consumer',idnum,'got=>',data)
>>> if __name__=='__main__':
for i in range(numconsumers):
thread.start_new_thread(consumer,(i,))
for i in range(numproducers):
thread.start_new_thread(producer,(i,))
time.sleep(((numproducers-1)*nummessages)+1)
print('Main thread exit.')
在此请注意队列是如何给一个全局变量赋值的.因为这一点,它可以在所有派生的线程中共享.因为这些线程改变对象而非变量名,所以队列对象作为参数传入线程中的函数也是可行的.队列是内存中的共享对象,无论采用是何种方式:
>>> dataQueue=queue.Queue()
>>> def producer(idnum,dataqueue):
for msgnum in range(nummessages):
time.sleep(dinum)
dataqueue.put('[producer id=%d,count=%d]'%(idnum,msgnum))
>>> def consumer(idnum,dataqueue):...
>>> numproducers=4
>>> if __name__=='__main__':
for i in range(numproducers):
thread.start_new_thread(producer,(i,dataQueue))
for i in range(numproducers):
thread.start_new_thread(producer,(i,dataQueue))
守护线程与退出:
>>> import threading,queue,time
>>> def producer(idnum,dataqueue):...
>>> def consumer(idnum,dataqueue):...
>>> if __name__=='__main__':
for i in range(numconsumers):
thread=threading.Thread(target=consumer,args=(i,dataQueue))
thread.daemon=True
thread.start()
waitfor=[]
for i in range(numproducers):
thread=threading.Thread(target=producer,args=(i,dataQueue))
waitfor.append(thread)
thread.start()
for thread in waitfor:thread.join()
print('Main thread exit.')
运行此脚本,可以试着调整脚本开头部分的参数试验不同的情景.
关于退出:
内建的sys.exit()函数在调用时可以让程序提前结束:
有意思的是,这个函数其实只抛出了一个内建的SystemExit异常.因此,我们可以像往常那样捕获这个异常,来拦截程序的过早退出并执行清理活动;如果未被捕获,则解释器正常退出.
>>> try:
sys.exit()
except SystemExit:
print('ignoring exit')
ignoring exit
上面代码俗称(钩子),防止意外关闭,毕竟是try…except…异常捕获语句程序.
下面的码表示了如何从进程中的函数里面退出:
>>> def later():
import sys
print('Bye sys world')
sys.exit(42)
print('Never reched')
>>> if __name__=='__main__':later()
Bye sys world
然后将上面代码保存为.py的文件,载入这个函数的程序可以将其退出异常捕获并且重写,或者编写一个负责清理的finally代码块,用于在程序退出处理时运行:
第一段:
>>> from testexit_sys import later #将上一段代码保存.py文件然后放到你的编译运行的正确目录下
>>> try:
later()
except SystemExit:
print('Ignored...')
Bye sys world
Ignored...
第二段:
>>> try:
later()
finally:
print('Cleanup')
Bye sys world
Cleanup
然后显示交互式会话进程的退出
接下来是os版本的退出模式,首先把os函数define一下:
def outahere():
import os
prinyt('out of os world')
os._exit(88)
print('Never reached')
>>> if __name__=='__main__':outahere()
第一次调用:
>>> from testexit_os import outahere
>>> try:
outahere()
except:
print('Ignored')
Ignored
第二次调用:
>>> from testexit_os import outahere
>>> try:
outahere()
finally:
print('Cleanup')
Cleanup
和sys.exit不同,try/except和try/finally截获对os._exit均不起作用.
下面在Linux系统下用os.system和os.Popen获得退出状态,我们先看一个shell命令的实例.下面的例子在Linux上运行通过管道读取输出流并获取他们的退出状态代码:
>>> import os
>>> pipe=os.popen('python testexit_sys.py')
>>> pipe.read()
''
>>> stat=pipe.close()
>>> stat
512
>>> hex=(stat)
>>> stat>>8
2
>>> pipe=os.popen('python testexit_os.py')
>>> stat=pipe.close()
>>> stat,stat >>8
(512, 2)
此类代码在Windows,mac os x,linux三大操作系统中均可运行.由于在内存中的地址差异可能导致输出的状态代码略有差异.退出状态不在位掩码中编码,此外还可以用sys.platform.