1.创建并销毁线程
#!/usr/bin/python #code to execute in an independent thread import time def countdown(n): while n > 0: print('T-minus',n) n -= 1 time.sleep(5) #create and launch a thread from threading import Thread t = Thread(target=countdown,args=(10,)) t.start()
# python concurency.py ('T-minus', 10) ('T-minus', 9) ('T-minus', 8) ('T-minus', 7) ('T-minus', 6) ('T-minus', 5) ('T-minus', 4) ('T-minus', 3) ('T-minus', 2) ('T-minus', 1)
创建一个线程实例后,需要调用start()让其运行。线程是以系统级别线程的方式执行,由操作系统管理。一旦执行,各个线程独立运行,直到目标函数返回结果,可以使用is_alive()查询一个线程实例是否正在运行。
if t.is_alive(): print('The thread is still running') else: print('Completed')
可以使用join()来请求结合一个线程
执行上述代码时,python解释器会等待所有线程结束。对于需要时间较长的线程或者后台任务,可以考虑将线程以daemon的方式运行
t.setDaemon(True)
不能结合以daemon方式运行的线程,当主线程终止时它们会自动销毁
如果想要能够终止线程,这些线程必须能够可编程在选择的点上能够退出
import time from threading import Thread class CountdownTask: def __init__(self): self._running=True def terminate(self): self._running=False def run(self,n): while self._running and n > 0: print('T-minus',n) n-=1 time.sleep(5) c=CountdownTask() t=Thread(target=c.run,args=(10,)) t.start() c.terminate() t.join()
由于Global Interpreter Lock(GIL)全局解释器锁的原因,Python线程被限制在任何给定时间内只能有一个线程可以执行。基于这个原因,Python线程不能用于那些需要大量计算的任务。Python线程更适合用于I/O处理,处理那些执行阻塞操作例如等待I/O,等待数据库操作结果等的代码的并发执行。
2.判断一个线程是否已经启动
Problem:
已经启动一个线程,但是想要知道它什么时候开始运行的
Solution:
Python线程的一个关键特性就是它们独立执行并且非确定性。如果程序的其他线程需要知道是否一个线程执行进一步操作之前已经达到某一个阶段。threading模块的Event对象可以帮助解决这个问题‘
#!/usr/bin/python from threading import Thread,Event import time #code to execute in an independent thread def countdown(n,started_evt): print('countdown starting') started_evt.set() while n > 0: print('T-minus',n) n -= 1 time.sleep(5) #create the event object that will be used to signal startup started_evt = Event() #launch the thread and pass the startup event print('launching countdown') t=Thread(target=countdown,args=(10,started_evt)) t.start() #wait for the thread to start started_evt.wait() print('countdown is running')
Event objects are best used for one-time events.
#!/usr/bin/python import threading import time class PerodicTimer: def __init__(self,interval): self._interval=interval self._flag=0 self._cv=threading.Condition() def start(self): t=threading.Thread(target=self.run) t.daemon=True t.start() def run(self): ''' Run the timer and notify waiting threads after each interval ''' while True: time.sleep(self._interval) with self._cv: self._flag ^= 1 self._cv.notify_all() def wait_for_tick(self): ''' wait for the next tick of the timer ''' with self._cv: last_flag=self._flag while last_flag == self._flag: self._cv.wait() #example use of the timer ptimer=PerodicTimer(5) ptimer.start() #two threads that synchronize on the timer def countdown(nticks): while nticks > 0: ptimer.wait_for_tick() print('T-minus',nticks) nticks-=1 def countup(last): n=0 while n < last: ptimer.wait_for_tick() print('Counting',n) n+=1 threading.Thread(target=countdown,args=(10,)).start() threading.Thread(target=countup,args=(5,)).start()
# python event2.py ('Counting', 0) ('T-minus', 10) ('T-minus', 9) ('Counting', 1) ('Counting', 2) ('T-minus', 8) ('Counting', 3) ('T-minus', 7) ('Counting', 4('T-minus', 6) ) ('T-minus', 5) ('T-minus', 4) ('T-minus', 3) ('T-minus', 2) ('T-minus', 1)
Event对象的一个很重要的特性就是它们会唤醒所有正在等待的线程。如果想要编写一个程序只唤醒单个正在等待的线程,更好使用Semaphore或者Condition对象
#!/usr/bin/python import threading #worker thread def worker(n,sema): #wait to be signaled sema.acquire() #do some work print('working',n) #create some threads sema=threading.Semaphore(0) nworkers=10 for n in range(nworkers): t=threading.Thread(target=worker,args=(n,sema,)) t.start()
如果运行这个程序,一组线程会被启动,但是没有任何事情发生。应为它们都被阻塞等待获取信号量。每次释放信号量,只有一个worker将被唤醒和运行
直接在python终端执行将无任何反应并且无法终止程序
# python semaphore.py ^C^C^C^C^C^C^C
在ipython终端中输入以上程序然后执行
In [8]: sema.release() In [9]: ('working', 0) In [9]: sema.release() In [10]: ('working', 1) In [10]: sema.release() ('working', 2) In [11]: sema.release() ('working' , 3) In [12]: sema.release() ('working', 4) In [13]: sema.release() ('working', 5) In [14]: sema.release() ('working', 6) In [15]: sema.release() In [16]: ('working', 7) In [16]: sema.release() In [17]: ('working', 8) In [17]: sema.release() ('working', 9) In [18]: sema.release() In [19]: sema.release()
3.线程间通信
Problem:
执行程序时开启了多个线程,现在需要在这些线程之间通信或者交换数据
Solution:
也许从一个线程发送数据到另一个线程的最安全的方式就是使用 Queue模块.可以创建一个 Queue实例用于所有的线程共享。这些线程然后使用put()或者get()操作来向队列中添加或者删除项目
#!/usr/bin/python from threading import Thread from Queue import Queue #a thread that produces data def producer(out_q): while True: #produce some data data="producer data" out_q.put(data) #a thread that comsumes data def consumer(in_q): while True: #get some data data=in_q.get() #process the data print data print "consumer" #create the shared queue and launch both threads q=Queue() t1=Thread(target=consumer,args=(q,)) t2=Thread(target=producer,args=(q,)) t1.start() t2.start()
执行结果:
producer data consumer producer data consumer producer data consumer producer data consumer producer data consumer producer data consumer producer data consumer producer data consumer ....... .......
以上代码会不断地循环执行。当使用队列时,协调关闭producer和comsumer会比较诡异。
参考文章:
http://chimera.labs.oreilly.com/books/1230000000393/ch12.html