1、3.3.1 Python的前后台线程的前后台线程线程类似于同时执行多个不同程序,多线程运行有如下优点:使用线程可以把占据长时间的程序中的任务放到后台去处理。程序的运行速度可能加快在一些等待的任务实现上如用户输入、文件读写和网络收发数据等,线程就比较有用了。在这种情况下我们可以释放一些珍贵的资源如内存占用等等。每个线程都有他自己的一组CPU寄存器,称为线程的上下文,该上下文反映了线程上次运行该线程的CPU寄存器的状态。在其他线程正在运行时,线程可以暂时搁置(也称为睡眠),这就是线程的退让。3.3.1 Python的前后台的前后台线程线程在Python中要启动一个线程,可以使用threading包
2、中的Thread建立一个对象,这个Thread类的基本原型是:t=Thread(target,args=None)其中target是要执行的线程函数,args是一个元组或者列表为target的函数提供参数,然后调用t.start()就开始了线程。例例3-3-1:在主线程中启动一个子线程执行在主线程中启动一个子线程执行reading函数。函数。import threadingimport timeimport random def reading():for i in range(10):print(reading,i)time.sleep(random.randint(1,2)r=thread
3、ing.Thread(target=reading)r.setDaemon(False)r.start()print(The End)程序结果:reading 0The Endreading 1reading 2reading 3reading 4从结果看到主线程启动子线程r后就结束了,但是子线程还没有结束,继续显示完reasing 4后才结束。其中的r.setDaemon(False)就是设置线程r为后台线程,后台线程不因主线程的结束而结束。如何设置r.setDaemon(True),那么r就是前台线程。例例3-3-2:启动一个前台线程启动一个前台线程import threadingimpo
4、rt timeimport random def reading():for i in range(5):print(reading,i)time.sleep(random.randint(1,2)r=threading.Thread(target=reading)r.setDaemon(True)r.start()print(The End)程序结果:reading 0The End由此可见在主线程结束后子线程也结束,这就是前台线程。例例3-3-3:前台与后台线程:前台与后台线程import threadingimport timeimport random def reading():fo
5、r i in range(5):print(reading,i)time.sleep(random.randint(1,2)def test():r=threading.Thread(target=reading)r.setDaemon(True)r.start()print(test end)t=threading.Thread(target=test)t.setDaemon(False)t.start()print(The End)程序结果:The Endreading 0test end由此可见主线程启动后台子线程t后就结束了,但是t还在执行,在t中启动前台r子线程,之后t结束,相应的r
6、也结束。3.3.2 线程的等待线程的等待3.3.2 线程的线程的等待等待在多线程的程序中往往一个线程(例如主线程)要等待其它线程执行完毕才继续执行,这可以用join函数,使用的方法是:线程对象.join()在一个线程代码中执行这条语句,当前的线程就会停止执行,一直等到指定的线程对象的线程执行完毕后才继续执行,即这条语句启动阻塞等待的作用。例例3-3-4:主线程启动一个子线程并等待子线程结束后才继续执行。:主线程启动一个子线程并等待子线程结束后才继续执行。import threadingimport timeimport random def reading():for i in range(5
7、):print(reading,i)time.sleep(random.randint(1,2)t=threading.Thread(target=reading)t.setDaemon(False)t.start()t.join()print(The End)程序结果:reading 0reading 1reading 2reading 3reading 4The End由此可见主线程启动子线程t执行reading函数,t.join()就阻塞主线程,一直等到t线程执行完毕后才结束t.join(),继续执行显示The End。例例3-3-5:在一个子线程启动另外一个子线程,并等待子线程结束后才
8、继续执行。:在一个子线程启动另外一个子线程,并等待子线程结束后才继续执行。import threadingimport timeimport random def reading():for i in range(5):print(reading,i)time.sleep(random.randint(1,2)def test():r=threading.Thread(target=reading)r.setDaemon(True)r.start()r.join()print(test end)t=threading.Thread(target=test)t.setDaemon(False)t
9、.start()t.join()print(The End)程序结果:reading 0reading 1reading 2reading 3reading 4test endThe End由此可见主线程启动t线程后t.join()会等待t线程结束,在test中再次启动r子线程,而且r.join()而阻塞t线程,等待r线程执行完毕后才结束r.join(),然后显示test end,之后t线程结束,再次结束t.join(),主线程显示The End后结束。3.3.3 多线程与资源多线程与资源3.3.3 多线程与资源多线程与资源在多个线程的程序中一个普遍存在的问题是,如果多个线程要竞争同时访问与改
10、写公共资源,那么应该怎么样协调各个线程的关系。一个普遍使用的方法是使用线程锁,Python使用threading.RLock类来创建一个线程锁对象:lock=threading.RLock()这个对象lock有两个重要方法是acquire()与release()。当执行:lock.acquire()语句时强迫lock获取线程锁,如果已经有另外的线程先调用了acquire()方法获取了线程锁而还没有调用release()释放锁,那么这个lock.acquire()就阻塞当前的线程,一直等待锁的控制权,直到别的线程释放锁后lock.acquire()就获取锁并解除阻塞,线程继续执行,执行后线程要调
11、用lock.release()释放锁,不然别的线程会一直得不到锁的控制权使用acquire/release的工作机制我们可以把一段修改公共资源的代码用acquire()与release()夹起来,这样就保证一次最多只有一个线程在修改公共资源,别的线程如果也要修改就必须等待,直到本线程调用release()释放锁后别的线程才能获取锁的控制权进行资源的修改。例例3-3-6:一个子线程:一个子线程A把一个全局的列表把一个全局的列表words进行升序的排列,另外一个进行升序的排列,另外一个D线程把这个列表进行降序的排列。线程把这个列表进行降序的排列。import threadingimport tim
12、e lock=threading._RLock()words=a,b,d,b,p,m,e,f,b def increase():global words for count in range(5):lock.acquire()print(A acquired)for i in range(len(words):for j in range(i+1,len(words):if wordsjwordsi:t=wordsi wordsi=wordsj wordsj=t print(D,words)time.sleep(1)lock.release()A=threading.Thread(target
13、=increase)A.setDaemon(False)A.start()D=threading.Thread(target=decrease)D.setDaemon(False)D.start()print(The End)程序结果:A acquiredA a,b,b,b,d,e,f,m,pThe EndD acquiredD p,m,f,e,d,b,b,b,aD acquiredD p,m,f,e,d,b,b,b,aA acquiredA a,b,b,b,d,e,f,m,pA acquiredA a,b,b,b,d,e,f,m,pD acquiredD p,m,f,e,d,b,b,b,aD acquiredD p,m,f,e,d,b,b,b,aD acquiredD p,m,f,e,d,b,b,b,aA acquiredA a,b,b,b,d,e,f,m,pA acquiredA a,b,b,b,d,e,f,m,p由此可见无论是increase还是decrease的排序过程,都是在获得锁的控制权下进行的,因此排序过程中另外一个线程必然处于等待状态,不会干扰本次的排序,因此每次显示的结构不是升序的就是降序的。