【文档说明】Python爬虫程序设计KC33.pptx,共(20)页,91.855 KB,由小橙橙上传
转载请保留链接:https://www.ichengzhen.cn/view-2418.html
以下为本文档部分文字说明:
3.3.1Python的前后台线程线程类似于同时执行多个不同程序,多线程运行有如下优点:•使用线程可以把占据长时间的程序中的任务放到后台去处理。•程序的运行速度可能加快•在一些等待的任务实现上如用户输入、文件
读写和网络收发数据等,线程就比较有用了。在这种情况下我们可以释放一些珍贵的资源如内存占用等等。•每个线程都有他自己的一组CPU寄存器,称为线程的上下文,该上下文反映了线程上次运行该线程的CPU寄存器的状态。•在其他线程正在运行时
,线程可以暂时搁置(也称为睡眠),这就是线程的退让。3.3.1Python的前后台线程在Python中要启动一个线程,可以使用threading包中的Thread建立一个对象,这个Thread类的基本原型是:t
=Thread(target,args=None)其中target是要执行的线程函数,args是一个元组或者列表为target的函数提供参数,然后调用t.start()就开始了线程。例3-3-1:在主线程中启动一个子线程执行reading函数。importthreadingimporttime
importrandomdefreading():foriinrange(10):print("reading",i)time.sleep(random.randint(1,2))r=threading.Thread(target=reading)r.setDaemon(False)r.star
t()print("TheEnd")程序结果:reading0TheEndreading1reading2reading3reading4从结果看到主线程启动子线程r后就结束了,但是子线程还没有结束,继续显示完reasing4后才结束。其中的r.setDaemon(Fal
se)就是设置线程r为后台线程,后台线程不因主线程的结束而结束。如何设置r.setDaemon(True),那么r就是前台线程。例3-3-2:启动一个前台线程importthreadingimporttimeimportrandomdefreading():for
iinrange(5):print("reading",i)time.sleep(random.randint(1,2))r=threading.Thread(target=reading)r.setDaemon(True)r.start()print("TheEnd")程序结果:rea
ding0TheEnd由此可见在主线程结束后子线程也结束,这就是前台线程。例3-3-3:前台与后台线程importthreadingimporttimeimportrandomdefreading():foriinrange(5):print("reading",i)time.sleep(rand
om.randint(1,2))deftest():r=threading.Thread(target=reading)r.setDaemon(True)r.start()print("testend")t=threading.Thread(t
arget=test)t.setDaemon(False)t.start()print("TheEnd")程序结果:TheEndreading0testend由此可见主线程启动后台子线程t后就结束了,但是t还在执行,在t中启动前台r子线程,之后t结束,相应的r也
结束。3.3.2线程的等待3.3.2线程的等待在多线程的程序中往往一个线程(例如主线程)要等待其它线程执行完毕才继续执行,这可以用join函数,使用的方法是:线程对象.join()在一个线程代码中执行这条语句,当前的线程就会停止执行,一直等到指定的线程对象的线程执行完毕后才继续执行,即这条语句
启动阻塞等待的作用。例3-3-4:主线程启动一个子线程并等待子线程结束后才继续执行。importthreadingimporttimeimportrandomdefreading():foriinrange(5):print("reading",i)time.
sleep(random.randint(1,2))t=threading.Thread(target=reading)t.setDaemon(False)t.start()t.join()print("TheEnd")程序结果:
reading0reading1reading2reading3reading4TheEnd由此可见主线程启动子线程t执行reading函数,t.join()就阻塞主线程,一直等到t线程执行完毕后才结束t.join(),继续执行显示TheEnd。例3-3-5:在一个
子线程启动另外一个子线程,并等待子线程结束后才继续执行。importthreadingimporttimeimportrandomdefreading():foriinrange(5):print("reading",i)time.sleep(random.randint
(1,2))deftest():r=threading.Thread(target=reading)r.setDaemon(True)r.start()r.join()print("testend")t=threading.Thread(target=test)
t.setDaemon(False)t.start()t.join()print("TheEnd")程序结果:reading0reading1reading2reading3reading4testendTheEnd由此可见主线程启动t线程后t.join()会等
待t线程结束,在test中再次启动r子线程,而且r.join()而阻塞t线程,等待r线程执行完毕后才结束r.join(),然后显示testend,之后t线程结束,再次结束t.join(),主线程显示TheEnd后结束。3.3.3多线程与资源3.3.3多线程与资源在多个线程的程
序中一个普遍存在的问题是,如果多个线程要竞争同时访问与改写公共资源,那么应该怎么样协调各个线程的关系。一个普遍使用的方法是使用线程锁,Python使用threading.RLock类来创建一个线程锁对象:lock=threading.RLock()这个对象lock有两个
重要方法是acquire()与release()。当执行:lock.acquire()语句时强迫lock获取线程锁,如果已经有另外的线程先调用了acquire()方法获取了线程锁而还没有调用release()释放锁,那么这个lock
.acquire()就阻塞当前的线程,一直等待锁的控制权,直到别的线程释放锁后lock.acquire()就获取锁并解除阻塞,线程继续执行,执行后线程要调用lock.release()释放锁,不然别的线程
会一直得不到锁的控制权使用acquire/release的工作机制我们可以把一段修改公共资源的代码用acquire()与release()夹起来,这样就保证一次最多只有一个线程在修改公共资源,别的线程如果也要修改就必须等待,直到本线程
调用release()释放锁后别的线程才能获取锁的控制权进行资源的修改。例3-3-6:一个子线程A把一个全局的列表words进行升序的排列,另外一个D线程把这个列表进行降序的排列。importthreadingimport
timelock=threading._RLock()words=["a","b","d","b","p","m","e","f","b"]defincrease():globalwordsforcountinrange(
5):lock.acquire()print("Aacquired")foriinrange(len(words)):forjinrange(i+1,len(words)):ifwords[j]<words[i
]:t=words[i]words[i]=words[j]words[j]=tprint("A",words)time.sleep(1)lock.release()defdecrease():globalwordsforcountinrange(5):lock.
acquire()print("Dacquired")foriinrange(len(words)):forjinrange(i+1,len(words)):ifwords[j]>words[i]:t=words[i]words[i]=words[j]words[j]=tpri
nt("D",words)time.sleep(1)lock.release()A=threading.Thread(target=increase)A.setDaemon(False)A.start()D=threading.T
hread(target=decrease)D.setDaemon(False)D.start()print("TheEnd")程序结果:AacquiredA['a','b','b','b','d','e',
'f','m','p']TheEndDacquiredD['p','m','f','e','d','b','b','b','a']DacquiredD['p','m','f','e','d','b','b','b','a']A
acquiredA['a','b','b','b','d','e','f','m','p']AacquiredA['a','b','b','b','d','e','f','m','p']DacquiredD['p','m','f','e','d','b','b','b','a']Dacquired
D['p','m','f','e','d','b','b','b','a']DacquiredD['p','m','f','e','d','b','b','b','a']AacquiredA['a','b','b','b','d','e','f','m','p'
]AacquiredA['a','b','b','b','d','e','f','m','p']由此可见无论是increase还是decrease的排序过程,都是在获得锁的控制权下进行的,因此排序过程
中另外一个线程必然处于等待状态,不会干扰本次的排序,因此每次显示的结构不是升序的就是降序的。