第9單元 進程和線程_第1頁
第9單元 進程和線程_第2頁
第9單元 進程和線程_第3頁
第9單元 進程和線程_第4頁
第9單元 進程和線程_第5頁
已閱讀5頁,還剩77頁未讀, 繼續(xù)免費閱讀

付費下載

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)

文檔簡介

第9單元

進程和線程任務(wù)9.1多進程繪制彩蓮和彩盤任務(wù)9.2鞭炮聲聲喜迎春任務(wù)9.3孤獨的小鞭炮學習目標知識目標技能目標素質(zhì)目標

了解進程和線程的概念。

掌握創(chuàng)建進程和線程的方法。

掌握進程間的通信方法。

了解線程生命周期、線程阻塞和守護線程的概念。

掌握線程安全與同步的實現(xiàn)方法。

能夠根據(jù)需要使用進程開發(fā)程序。

能夠在進程間實現(xiàn)通信。

能夠根據(jù)需要使用線程開發(fā)程序。

能夠使用常見的線程同步方式實現(xiàn)線程安全。

培養(yǎng)標準化的編碼規(guī)范能力。

培養(yǎng)創(chuàng)新能力以及分析問題和解決問題的能力。

培養(yǎng)團隊意識和溝通能力。

培養(yǎng)效率意識、時間成本意識。教學內(nèi)容任務(wù)9.1多進程繪制彩蓮和彩盤一、任務(wù)描述turtle庫可以同時開啟多個窗口,分別完成不同的任務(wù)。使用海龜作圖庫turtle,在兩個窗口中同時繪圖,一個繪制彩蓮,另一個繪制彩盤,效果如圖所示。

幾乎所有的操作系統(tǒng)都支持同時運行多個任務(wù),每個任務(wù)通常是一個程序,每一個運行中的程序就是一個進程,即進程是程序的運行實例?,F(xiàn)在的操作系統(tǒng)幾乎都支持多進程并發(fā)運行。二、相關(guān)知識1.進程

在Windows操作系統(tǒng)下打開任務(wù)管理器,單擊任務(wù)管理器窗口中的“進程”選項卡查看計算機中所有的進程。二、相關(guān)知識操作系統(tǒng)調(diào)度并執(zhí)行程序,這個“執(zhí)行中的程序”稱為進程。進程是操作系統(tǒng)進行資源分配和調(diào)度的基本單位。

程序被執(zhí)行后成為了一個活動的實體,這個實體就是進程。二、相關(guān)知識通過Process類的構(gòu)造方法Process()可以創(chuàng)建一個代表子進程的Process對象,該方法的聲明如下:Process(group=None,

target=None,

name=None,

args=(),

kwargs={},

*,

daemon=None)group

--必須為None,為以后擴展功能保留的參數(shù)。target--表示子進程的功能函數(shù),用于為子進程分派任務(wù)。name--表示當前進程的名稱。args--target指定函數(shù)的位置參數(shù)kwargs--傳入target指定函數(shù)的關(guān)鍵字參數(shù)daemon--表示是否將進程設(shè)置為守護進程二、相關(guān)知識2.通過Process類創(chuàng)建子進程(1)通過Process類創(chuàng)建子進程進程創(chuàng)建好了,需要用start()啟動frommultiprocessingimportProcess

defdo_work():

print('Higays')

if__name__=='__main__':

process=Process(target=do_work)

process.start()

print(process)frommultiprocessingimportProcess

defdo_work(message):

print('Higays')

print(message)

if__name__=='__main__':

process=Process(target=do_work,name='qq1',args=('xiaoming',))

process.start()

print(process)frommultiprocessingimportProcess

defdo_work(message):

print('Higays')

print(message)

if__name__=='__main__':

process=Process(target=do_work,name='qq1',kwargs={'message':'xiaoming'})

process.start()

print(process)二、相關(guān)知識Windows系統(tǒng)中使用multiprocessing模塊時,必須采用“if__name__==’__main__’”的方式運行程序。二、相關(guān)知識'__name__'

Python

中一個特殊的內(nèi)置類屬性:作為模塊被導入時的'__name__':當Python程序作為一個模塊被另一個程序?qū)霑r,'__name__'

的值是該模塊的文件名,即'.py'

之前的目錄名稱。直接運行時的'__name__':如果Python程序作為主程序(不是作為模塊被導入)直接執(zhí)行,'__name__'

的默認值為"__main__"。'__name__'

的應(yīng)用:在代碼中,可以使用'if__name__=="__main__"'

來判斷是否應(yīng)該執(zhí)行特定的代碼塊。如果'__name__'

等于'"__main__"',說明腳本正在作為主程序直接運行;否則,它將被當作模塊進行導入。子進程不會運行if__name__==’__main__’中的程序classMyProcess(Process):def__init__(self,interval):Process.__init__(self)erval=intervaldefrun(self):time_start=time.time()time.sleep(erval)time_stop=time.time()print("子進程%s執(zhí)行結(jié)束,耗時%0.2f秒"%(os.getpid(),time_stop-time_start))自定義一個繼承自Process類的子類,調(diào)用子類的構(gòu)造方法亦可創(chuàng)建子進程。if__name__=='__main__':

process=MyProcess(5)

process.start()(2)通過Process類的子類創(chuàng)建子進程二、相關(guān)知識多進程模塊multiprocessing中提供了Pool(進程池)類,通過Pool類的構(gòu)造方法Pool()可以批量創(chuàng)建子進程。Pool(processes=None,initializer=None,initargs=(),maxtasksperchild=None,context=None)processes

--表示進程的數(shù)量。若processes參數(shù)設(shè)為None,則會使用os.cpu_count()返回的結(jié)果。二、相關(guān)知識3.通過Pool類創(chuàng)建子進程Pool類中提供了一些操作進程池的方法,關(guān)于這些方法的說明如下表所示。二、相關(guān)知識apply_async(self,func,args=(),kwds={},callback=None,error_callback=None)通過Pool類的apply_async()方法可以采用非阻塞的方式給進程池中的進程添加任務(wù)。func--表示函數(shù)名稱。args和kwds--表示函數(shù)func接收的參數(shù)。callback--表示程序執(zhí)行成功后調(diào)用的函數(shù)。二、相關(guān)知識(1)進程池非阻塞式添加任務(wù)若希望主進程能等待所有的子進程執(zhí)行完之后結(jié)束,需要通過join()方法將主進程切換成阻塞狀態(tài)。pool.close()#關(guān)閉進程池pool.join()#阻塞主進程二、相關(guān)知識frommultiprocessingimportPoolimporttimeimportosdefaction(name='張三',num=1):

print(name,'運行第',num,'項任務(wù),當前進程ID:',os.getpid())

time.sleep(3)if__name__=='__main__':

#創(chuàng)建包含4個進程的進程池

pool=Pool(processes=4)#創(chuàng)建進程池,指定最大進程數(shù)為4

#將action()函數(shù)分4次提交給進程池

pool.apply_async(action)#進程添加、運行任務(wù),使用默認參數(shù)

pool.apply_async(action,args=('李四',2))

pool.apply_async(action,args=('王五',3))

pool.apply_async(action,kwds={'name':'馬六','num':4})

pool.close()#關(guān)閉進程池

pool.join()#阻塞主進程示例:創(chuàng)建了一個具有4個工作進程的進程池并采用非阻塞式向進程池中添加了4個子進程,最后關(guān)閉進程池,調(diào)用join()方法阻塞主進程,待所有子進程結(jié)束后,主進程才退出二、相關(guān)知識通過Pool類的apply()

方法可以采用阻塞的方式給進程池中的進程添加任務(wù)。apply(self,func,args=(),kwds={})(2)進程池阻塞式添加任務(wù)二、相關(guān)知識示例:修改上面的非阻塞式代碼,實現(xiàn)阻塞式向進程池中添加任務(wù)二、相關(guān)知識frommultiprocessingimportPoolimporttimeimportosdefaction(name='張三',num=1):

print(name,'運行第',num,'項任務(wù),當前進程ID:',os.getpid())

time.sleep(3)if__name__=='__main__':

#創(chuàng)建包含4個進程的進程池

pool=Pool(processes=4)#創(chuàng)建進程池,指定最大進程數(shù)為4

#將action()函數(shù)分4次提交給進程池

pool.apply(action)#進程添加、運行任務(wù),使用默認參數(shù)

pool.apply(action,args=('李四',2))

pool.apply(action,args=('王五',3))

pool.apply(action,kwds={'name':'馬六','num':4})

pool.close()#關(guān)閉進程池

每個進程中所擁有的數(shù)據(jù)(包括全局變量)都是獨有的,無法與其它進程共享。進程A進程B數(shù)據(jù)數(shù)據(jù)二、相關(guān)知識4.進程間的通信使用Queue類實現(xiàn)進程間通信的方式,就是各個進程使用操作系統(tǒng)開辟一個隊列空間,把數(shù)據(jù)放到該隊列中,當然也可以從該隊列中把自己需要的信息取出。二、相關(guān)知識(1)使用Queue類實現(xiàn)進程間通信方法功能put(obj[,block=True[,timeout=None]])將obj放入隊列。當block參數(shù)為True時,一旦隊列寫滿,則當前進程會被阻塞,直到有其他進程從該隊列中取走數(shù)據(jù)并騰出空間供obj使用。put_nowait(obj)該方法的功能等同于put(obj,False)get([block=True[,timeout=None]])從隊列中取數(shù)據(jù)并返回。當block為True且timeout為None時,該方法會阻塞當前進程,直到隊列中有可用的數(shù)據(jù)。如果block為False,則當前進程會直接執(zhí)行取數(shù)據(jù)的操作,如果取數(shù)據(jù)失敗,則拋出queue.Empty異常(這種情形下,timeout參數(shù)將不起作用)。get_nowait()該方法的功能等同于get(False)empty()判斷當前隊列空間是否為空,如果為空,則返回True;否則,返回Falseput()方法的聲明如下:put()方法put(item,

block=True,

timeout=None)item--表示向隊列中寫入的數(shù)據(jù)。block--

表示是否阻塞隊列。timeout--

表示超時時長,默認為None。二、相關(guān)知識當調(diào)用put()方法向隊列中寫入數(shù)據(jù)時,若將block參數(shù)設(shè)為True、timeout參數(shù)設(shè)為正值,則隊列在裝滿數(shù)據(jù)后會先阻塞timeout指定的時長,并在超時后拋出Queue.Full異常;若將block參數(shù)設(shè)為False,則隊列在裝滿數(shù)據(jù)后會立即拋出Queue.Full異常。二、相關(guān)知識get()方法的聲明如下:get()方法get(block=True,timeout=None)block--

表示是否阻塞隊列。timeout--

表示超時時長,默認為None。二、相關(guān)知識當調(diào)用get()方法從空隊列中讀取數(shù)據(jù)時,若將block參數(shù)設(shè)為True且timeout參數(shù)設(shè)為正值,則隊列在等待timeout指定的時長后再拋出Queue.Empty異常;若將block參數(shù)設(shè)為False,則會隊列會立即拋出Queue.Empty異常。二、相關(guān)知識二、相關(guān)知識frommultiprocessingimportProcess,Queuedefwrite(queue):

foriinrange(10):#循環(huán)將數(shù)據(jù)插入隊列

queue.put(i)defread(queue):

count=0

whileTrue:

num=queue.get()#讀取隊列中的數(shù)據(jù)

print('隊列中獲取的數(shù)據(jù)為:',num)

count=count+1

ifcount==10:#全部讀取完畢后退出

break

if__name__=='__main__':

queue=Queue()#創(chuàng)建隊列,隊列的長度沒有限制

#創(chuàng)建兩個進程分別運行函數(shù)write()和read()

process1=Process(target=write,args=(queue,))#寫進程

process2=Process(target=read,args=(queue,))#讀進程

process1.start()#啟動子進程

process2.start()通過隊列實現(xiàn)兩個進程間的數(shù)據(jù)共享。通常情況下,管道有2個端口,而Pipe類也常用來實現(xiàn)2個進程之間的通信,這2個進程分別位于管道的兩端,一端用來發(fā)送數(shù)據(jù),另一端用來接收數(shù)據(jù)。二、相關(guān)知識(2)使用Pipe類實現(xiàn)進程間通信conn1,conn2=multiprocessing.Pipe([duplex=True])conn1和conn2分別表示Pipe()函數(shù)返回的2個端口duplex參數(shù)默認為True,表示該管道是雙向的,如果為False,則表示管道是單向的,conn1只能用來接收數(shù)據(jù),而conn2只能用來發(fā)送數(shù)據(jù)。conn1只能用來接收數(shù)據(jù),而conn2只能用來發(fā)送數(shù)據(jù)。conn1和conn2都屬于PipeConnection對象,它們還可以調(diào)用表中所示的方法。二、相關(guān)知識方法功能send(obj)發(fā)送一個obj給管道的另一端,另一端使用recv()方法接收。需要說明的是,該obj必須是可序列化的,如果該obj序列化之后超過32MB,則很可能會引發(fā)ValueErrorrecv()接收管道另一端通過send()方法發(fā)送的數(shù)據(jù)close()關(guān)閉連接poll([timeout])返回連接中是否還有數(shù)據(jù)可以讀取send_bytes(buffer[,offset[,size]])發(fā)送字節(jié)數(shù)據(jù)。如果沒有指定offset、size參數(shù),則默認發(fā)送buffer的全部數(shù)據(jù);如果指定了offset和size參數(shù),則只發(fā)送buffer中從offset位置開始、長度為size的字節(jié)數(shù)據(jù)。通過該方法發(fā)送的數(shù)據(jù),應(yīng)該使用recv_bytes()或recv_bytes_into()方法接收recv_bytes([maxlength])接收通過send_bytes()方法發(fā)送的數(shù)據(jù),maxlength指定最多接收的字節(jié)數(shù)。該方法返回接收到的字節(jié)數(shù)據(jù)recv_bytes_into(buffer[,offset])功能與recv_bytes()方法類似,只是該方法將接收到的數(shù)據(jù)放在buffer中二、相關(guān)知識frommultiprocessingimportProcess,Pipedefwrite(conn1):

foriinrange(10):#將數(shù)據(jù)循環(huán)發(fā)送到管道的另一端

conn1.send(i)defread(conn2):

count=0

whileTrue:

num=conn2.recv()

print('從管道中獲取的數(shù)據(jù)為:',num)

count=count+1

ifcount==10:#全部讀取完畢后退出

breakif__name__=='__main__':

#創(chuàng)建管道

conn1,conn2=Pipe()#創(chuàng)建兩個進程分別運行函數(shù)write()和read()

process1=Process(target=write,args=(conn1,))#寫進程

process2=Process(target=read,args=(conn2,))#讀進程

#啟動子進程

process1.start()

process2.start()修改使用隊列實現(xiàn)進程間通信的程序,使用Pipe類實現(xiàn)2個進程之間的通信。三、任務(wù)分析

在兩個窗口中同時繪圖,需要用到Python的多進程技術(shù),需要在此任務(wù)中使用multiprocessing庫的Process類。首先定義兩個函數(shù),一個用于繪制彩蓮,另一個用于繪制彩盤。然后在主進程中創(chuàng)建兩個子進程,一個通過調(diào)用繪制彩蓮的函數(shù)繪制彩蓮,另一個通過調(diào)用繪制彩盤的函數(shù)繪制彩盤。觀察效果圖,可以看出彩蓮和彩盤顏色都是漸變的,因此需要用到coloradd庫。四、任務(wù)實現(xiàn)(1)在PyCharm中,右擊左側(cè)列表中的項目名稱“chapter09”,選擇“New”→“PythonFile”,在彈出的對話框中將文件命名為“9-1多進程繪制彩蓮和彩盤.py”,按“Enter”鍵,進入代碼編輯界面。(2)在新建文件中導入庫。除了導入海龜作圖庫turtle,還需要導入multiprocessing、coloradd庫。(3)設(shè)置窗體的常規(guī)屬性,即顏色模式為“255”、畫筆形狀為“turtle”、畫筆寬度為“5”、海龜移動速度為最快。importcoloraddfrommultiprocessingimportProcessimportturtleturtle.colormode(255) #設(shè)置顏色模式turtle.shape("turtle") #設(shè)置畫筆形狀turtle.pensize(5) #設(shè)置畫筆寬度turtle.speed(0) #設(shè)置海龜移動速度為最快四、任務(wù)實現(xiàn)(4)定義繪制彩蓮的函數(shù)。#繪制彩蓮defdrawlotus():

turtle.title("繪制彩蓮")

turtle.setup(500,500,10,50)#設(shè)置窗口大小和位置

fordin[20,10]:#繪制2朵大小不同的花朵,先繪制大的

"""畫一輪圖案"""

forkinrange(20):#循環(huán)繪制20個花瓣

foriinrange(10,0,-1):#繪制由里向外的花瓣邊緣

turtle.pencolor(coloradd.colorset(i*8))#設(shè)置顏色漸變

turtle.fd(d) #前進

turtle.left(9) #旋轉(zhuǎn)一定角度,繪制完花瓣剛好旋轉(zhuǎn)90°

turtle.left(90) #再旋轉(zhuǎn)90°,開始繪制花瓣的另一邊

foriinrange(10): #繪制由外向里的花瓣邊緣

turtle.pencolor(coloradd.colorset(i*8))#設(shè)置顏色漸變

turtle.fd(d)

turtle.left(9)

turtle.left(90)

turtle.left(360/20)

turtle.hideturtle() #隱藏海龜形狀

turtle.mainloop() #保持窗口不關(guān)閉四、任務(wù)實現(xiàn)(5)定義繪制彩盤的函數(shù)。#繪制彩盤defdrawplate():

turtle.title("繪制彩盤")

turtle.setup(500,500,610,50) #設(shè)置窗口大小和位置

color=(255,0,0) #顏色為紅色

foriinrange(360): #迭代360次

turtle.pencolor(color) #設(shè)定畫筆顏色

turtle.fd(200) #海龜前進200個單位長度

turtle.bk(200) #海龜?shù)雇?00個單位長度

turtle.right(1) #海龜右轉(zhuǎn)1°

color=coloradd.addcolor(color,0.01) #顏色值增加0.01

turtle.hideturtle()

turtle.mainloop()四、任務(wù)實現(xiàn)(6)創(chuàng)建兩個子進程,啟動子進程分別運行兩個函數(shù)。運行程序后,移動窗口,可以看到有3個窗口,一個繪制彩蓮,一個繪制彩盤,還有一個是主進程窗口。if__name__=='__main__':

myprocess=Process(target=drawlotus)#創(chuàng)建子進程

#啟動子進程

myprocess.start()

myprocess1=Process(target=drawplate)#創(chuàng)建子進程

#啟動子進程

myprocess1.start()任務(wù)9.2鞭炮聲聲喜迎春一、任務(wù)描述

春節(jié)期間,為減少環(huán)境污染,可以制作電子煙花慶祝新年。現(xiàn)有6張GIF格式的圖片,如圖a所示。使用海龜作圖turtle和threading庫,實現(xiàn)鞭炮聲聲喜迎春的動畫效果,如圖b、c所示,在動畫中鞭炮的爆炸速度、爆炸位置都是隨機生成的。

abc線程是系統(tǒng)進行運算調(diào)度的最小單位,也被稱為輕量級進程,它包含在進程之中,是進程的實際運作單位。進程中可以包含多個線程,每個線程是進程中單一順序的控制流,可以并行執(zhí)行不同的任務(wù)。二、相關(guān)知識1.線程線程一般可分為以下幾種類型:主線程程序啟動時,操作系統(tǒng)在創(chuàng)建進程的同時會立即運行一個線程,該線程通常被稱為主線程。子線程程序中創(chuàng)建的其它線程。守護線程守護線程是在后臺為其它線程提供服務(wù)的線程,它獨立于程序,不會因程序的終止而結(jié)束。前臺線程相對于守護線程的其它線程稱為前臺線程。二、相關(guān)知識線程與進程相似,也具有五個狀態(tài),分別是新建態(tài)、就緒態(tài)、運行態(tài)、阻塞態(tài)和消亡態(tài)。二、相關(guān)知識2.線程生命周期模塊threading中定義了Thread類,該類專門用于管理線程。可以直接通過Thread類的構(gòu)造方法Thread()創(chuàng)建線程,該方法的聲明如下:Thread(group=None,target=None,name=None,args=(),kwargs={},*,daemon=None)二、相關(guān)知識3.創(chuàng)建線程(1)使用Thread類創(chuàng)建線程使用Thread()方法創(chuàng)建的線程默認是前臺線程。前臺線程的特點是主線程會等待其執(zhí)行結(jié)束后終止程序。二、相關(guān)知識創(chuàng)建線程的示例如下:二、相關(guān)知識importthreadingdefwork1():#定義函數(shù)work1()循環(huán)輸出500次

foriinrange(500):

print(threading.currentThread().name,"輸出",i)defwork2():#定義函數(shù)work2()循環(huán)輸出500次

foriinrange(500,1000):

print(threading.currentThread().name,"輸出",i)defwork3():#定義函數(shù)work3()循環(huán)輸出500次

foriinrange(1000,1500):

print(threading.currentThread().name,"輸出",i)#創(chuàng)建線程thread1=threading.Thread(target=work1)#創(chuàng)建子線程,運行work1()函數(shù)thread1.start()#啟動子線程thread2=threading.Thread(target=work2)#創(chuàng)建子線程,運行work2()函數(shù)thread2.start()#啟動子線程work3()#主線程運行work3()函數(shù)使用構(gòu)造方法Thread()創(chuàng)建線程時,還可以將daemon參數(shù)設(shè)為True,創(chuàng)建一個后臺線程。thread_two=Thread(target=task,daemon=True)二、相關(guān)知識二、相關(guān)知識(2)使用Thread類的子類創(chuàng)建線程

可以自定義一個繼承自Thread的子類,在該子類中重寫run()方法,再利用子類的構(gòu)造方法創(chuàng)建線程。importthreadingclassThread1(threading.Thread):#定義循環(huán)輸出0~499的線程

def__init__(self,num):

super().__init__()

='線程-'+str(num)

defrun(self):

foriinrange(500):

print(,"輸出",i)classThread2(threading.Thread):#定義循環(huán)輸出500~999的線程

def__init__(self,num):

super().__init__()

='線程-'+str(num)

defrun(self):

foriinrange(500,1000):

print(,"輸出",i)defwork3():#定義函數(shù)work3()循環(huán)輸出1000~1499

foriinrange(1000,1500):

print(threading.currentThread().name,"輸出",i)thread1=Thread1(1)#創(chuàng)建子線程,運行work1()函數(shù)#創(chuàng)建線程thread1.start()#啟動子線程thread2=Thread2(2)#創(chuàng)建子線程,運行work2()函數(shù)thread2.start()#啟動子線程work3()#主線程運行work3()函數(shù)三、任務(wù)分析

鞭炮爆炸效果實際是6張圖片在同一坐標處依次顯示的結(jié)果,可將圖片設(shè)置成海龜形狀,通過依次改變海龜形狀實現(xiàn)爆炸效果。動畫中鞭炮在多個點同時爆炸,是并發(fā)的顯示效果,使用多線程可以實現(xiàn)鞭炮同時爆炸的效果。四、任務(wù)實現(xiàn)(1)在PyCharm中,右擊左側(cè)列表中的項目名稱“chapter09”,選擇“New”→“PythonFile”,在彈出的對話框中將文件命名為“9-2鞭炮聲聲喜迎春.py”,按“Enter”鍵,進入代碼編輯界面。(2)在新建文件中導入庫,除了導入海龜作圖庫turtle,還需要導入time、random和threading庫。(3)設(shè)置屏幕的繪畫延時為0ms。importturtleimporttime #導入time庫fromrandomimportrandint,random #從random庫導入隨機取整數(shù)命令fromthreadingimportThread #從threading庫導入Thread類turtle.delay(0)#屏幕的繪畫延時為0ms四、任務(wù)實現(xiàn)(4)定義存放圖片名稱的列表,使用循環(huán)將圖片名稱加入列表,同時將圖片設(shè)置為海龜形狀。eps=[]#定義圖片列表foriinrange(6):

eps.append(f"explosion/{i}.gif")#將圖片加入列表

turtle.addshape(f"explosion/{i}.gif")#將圖片設(shè)置為海龜形狀defexplode():#定義爆炸函數(shù)

time.sleep(random()) #隨機休眠一段時間

t=turtle.Turtle() #創(chuàng)建一個海龜對象

t.speed(0) #設(shè)置海龜移動速度為最快

t.penup() #抬起畫筆

whileTrue:

x=randint(-300,300)#隨機生成x坐標

y=randint(-300,300)#隨機生成y坐標

t.goto(x,y)#海龜移動到隨機生成的坐標處

foreineps:#依次改變海龜形狀,間隔0.02s

t.shape(e)

time.sleep(0.02)(5)定義爆炸函數(shù)。先隨機休眠一段時間,創(chuàng)建一個海龜對象,設(shè)置海龜移動速度為最快,抬筆。循環(huán)執(zhí)行:隨機生成坐標,并將海龜移動到坐標處,在該坐標處依次改變海龜形狀,實現(xiàn)爆炸效果,通過改變休眠時間控制爆炸速度。四、任務(wù)實現(xiàn)(6)循環(huán)創(chuàng)建10個線程,運行爆炸函數(shù)。至此,鞭炮聲聲喜迎春動畫編碼全部完成,運行并查看效果。forxinrange(10):#循環(huán)創(chuàng)建10個線程,運行爆炸函數(shù)

thread=Thread(target=explode)

thread.start()turtle.mainloop()#進入主循環(huán)任務(wù)9.3孤獨的小鞭炮一、任務(wù)描述在任務(wù)9.2的基礎(chǔ)上進行修改,實現(xiàn)同一時刻只有一只鞭炮爆炸的效果,如圖所示。

為了避免線程處于無休止的阻塞態(tài),可以調(diào)用join()方法指定等待的時長。join(timeout=None)timeout參數(shù)表示以秒為單位的超時時長。若timeout參數(shù)設(shè)為None,則線程會一直處于阻塞態(tài),直至消亡。二、相關(guān)知識1.線程阻塞與守護線程二、相關(guān)知識importtimeimportthreadingdeftask():#子線程運行的任務(wù)

time.sleep(2)

print("子線程%s:結(jié)束"%threading.currentThread().name)foriinrange(5):

#創(chuàng)建子線程,指定線程運行task()函數(shù)

thread=threading.Thread(target=task)

thread.start()#啟動子線程

thread.join()#阻塞子線程,直至消亡print("主線程結(jié)束")示例:創(chuàng)建多個子線程,阻塞主線程,按照創(chuàng)建的順序逐個運行完每個線程的任務(wù),最后結(jié)束主線程Python還支持守護線程(后臺線程),當程序的主線程及所有非守護線程運行結(jié)束時,未運行完畢的守護線程也會隨之消亡,程序結(jié)束運行,守護線程本質(zhì)也是線程。將普通線程設(shè)為守護線程,需通過線程對象調(diào)用其daemon屬性,并將該屬性的值設(shè)為True。線程對象調(diào)用daemon屬性必須在調(diào)用start()方法之前。二、相關(guān)知識二、相關(guān)知識importthreading#定義線程要調(diào)用的方法defaction(length):

forarcinrange(length):

#調(diào)用getName()方法獲取當前運行該程序的線程名

print(threading.current_thread().getName()+""+str(arc))#創(chuàng)建線程thread=threading.Thread(target=action,args=(20,))#將thread設(shè)置為守護線程thread.daemon=True#啟動線程thread.start()#主線程執(zhí)行如下語句foriinrange(5):

print(threading.current_thread().getName())示例:如何創(chuàng)建一個守護線程假設(shè)售票廳有100張火車票,它同時開啟兩個窗口(視為線程)賣票,每出售一張火車票顯示當前的剩余票數(shù)。由于兩個窗口共同修改同一份車票資源,容易導致票數(shù)混亂。二、相關(guān)知識2.線程安全與同步

在同一個程序的多個線程之間共享數(shù)據(jù),在一定程度上減少了資源的開銷。但因為多個線程訪問同一資源,可能會造成資源不同步的問題。為了解決這類問題,Python中引入了互斥鎖和可重入鎖,保證任一時刻只能有一個線程訪問共享的數(shù)據(jù)?;コ怄i可重入鎖二、相關(guān)知識互斥鎖是最簡單的加鎖技術(shù),它只有兩種狀態(tài):鎖定(locked)和非鎖定(unlocked)。當某個線程需要更改共享數(shù)據(jù)時,它會先對共享數(shù)據(jù)上鎖,將當前的資源轉(zhuǎn)換為“鎖定”狀態(tài),其它線程無法對被鎖定的共享數(shù)據(jù)進行修改;當線程執(zhí)行結(jié)束后,它會解鎖共享數(shù)據(jù),將資源轉(zhuǎn)換為“非鎖定”狀態(tài),以便其它線程可以對資源上鎖后進行修改。二、相關(guān)知識(1)互斥鎖在售賣車票的示例中加入互斥鎖,示意圖如下。每個窗口在修改剩余票數(shù)前都會上鎖,確保同一時刻只能自己訪問剩余票數(shù),一旦修改完票數(shù)之后,就對剩余票數(shù)進行解鎖。二、相關(guān)知識threading模塊中提供了一個Lock類,通過Lock類的構(gòu)造方法可以創(chuàng)建一個互斥鎖。mutex_lock=threading.Lock()二、相關(guān)知識Lock類中定義了acquire()和release()兩個方法,分別用于鎖定和釋放共享數(shù)據(jù)。acquire()方法可以設(shè)置鎖定共享數(shù)據(jù)的時長,其聲明如下:acquire(blocking=True,timeout=-1)blocking參數(shù)代表是否阻塞當前線程,若設(shè)為True(默認),則會阻塞當前線程至資源處于非鎖定狀態(tài);若設(shè)為False,則不會阻塞當前線程。二、相關(guān)知識處于鎖定狀態(tài)的互斥鎖調(diào)用acquire()方法會再次對資源上鎖,處于非鎖定狀態(tài)的互斥鎖調(diào)用release()方法會拋出RuntimeError異常。二、相關(guān)知識售票廳共有100張票,同時開啟兩個窗口賣票,每個窗口都實時顯示剩余票數(shù)二、相關(guān)知識fromthreadingimportThread,Lockimportthreadingtotal_ticket=100#總票數(shù)defsale_ticket():#售票函數(shù)

globaltotal_ticket

whiletotal_ticket>0:#有剩余火車票

mutex_lock.acquire()#資源上鎖,將互斥鎖設(shè)置為鎖定狀態(tài)

iftotal_ticket>0:

total_ticket-=1#票數(shù)減1

print('%s賣出一張票'%threading.current_thread().name)

print('剩余票數(shù):%d'%total_ticket)

mutex_lock.release()#解鎖if__name__=='__main__':

mutex_lock=Lock()#創(chuàng)建互斥鎖

thread_one=Thread(target=sale_ticket(),name='窗口1')

thread_one.start()

thread_two=Thread(target=sale_ticket(),name='窗口2')

thread_two.start()死鎖是指兩個或兩個以上的線程在執(zhí)行過程中,由于各自持有一部分共有資源或者彼此通信而造成的一種阻塞的現(xiàn)象。若沒有外力作用,線程們將無法繼續(xù)執(zhí)行,一直處于阻塞狀態(tài)。二、相關(guān)知識(2)死鎖在使用Lock對象給資源加鎖時,若操作不當很容易造成死鎖。常見的不當行為主要包括:(1)上鎖與解鎖的次數(shù)不匹配。(2)兩個線程各自持有一部分共享資源。二、相關(guān)知識示例:上鎖與解鎖次數(shù)不匹配defdo_work():mutex_lock.acquire()mutex_lock.acquire()mutex_lock.release()if__name__=='__main__':mutex_lock=Lock()thread=Thread(target=do_work)thread.start()程序執(zhí)行后始終無法結(jié)束,只能主動停止運行。二、相關(guān)知識示例:兩個線程互相使用對方的互斥鎖classThreadOne(Thread):defrun(self):iflock_a.acquire():......iflock_b.acquire():......lock_b.release()lock_a.release()classThreadTwo(Thread):defrun(self):iflock_b.acquire():......iflock_a.acquire():......lock_a.release()lock_b.release()二、相關(guān)知識fromthreadingimportThread,Lock

importtime

classThreadOne(Thread):

defrun(self):

iflock_a.acquire():

print(+':lock_a上鎖')

time.sleep(1)

iflock_b.acquire():

print(+':lock_b上鎖')

lock_b.release()

lock_a.release()classThreadTwo(Thread):

defrun(self):

iflock_b.acquire():

print(+':lock_b上鎖')

time.sleep(1)

iflock_a.acquire():

print(+':lock_a上鎖')

lock_a.release()

lock_b.release()

if__name__=='__main__':

lock_a=Lock()

lock_b=Lock()

thread_one=ThreadOne(name='線程1')

thread_two=ThreadTwo(name='線程2')

thread_two.start()

thread_one.start()二、相關(guān)知識若產(chǎn)生像第二種死鎖的情況,可以設(shè)置鎖定的時長,即調(diào)用acquire()方法時為timeout參數(shù)傳入值。lock_b.acquire(timeout=2)二、相關(guān)知識RLock類代表可重入鎖,它允許同一線程多次鎖定和多次釋放。通過RLock類的構(gòu)造方法可以創(chuàng)建一個可重入鎖對象。r_lock=RLock()二、相關(guān)知識(3)可重入鎖RLock類中包含以下三個重要的屬性:_block,表示內(nèi)部的互斥鎖。_owner,表示可重入鎖的持有者的線程ID。_count,表示計數(shù)器,用于記錄鎖被持有的次數(shù)。針對RLock對象的持有線程(屬主線程),每上鎖一次計數(shù)器就+1,每解鎖一次就-1。若計數(shù)器為0,則釋放內(nèi)部的鎖,這時其他線程可以獲取內(nèi)部的互斥鎖,繼而獲取RLock對象。二、相關(guān)知識可重入鎖的實現(xiàn)原理是通過為每個內(nèi)部鎖關(guān)聯(lián)計數(shù)器和屬主線程。當計數(shù)器為0時,內(nèi)部鎖處于非鎖定狀態(tài),可以被其它線程持有;當線程持有一個處于非鎖定狀態(tài)的鎖時,它將被記錄為鎖的持有線程,計數(shù)器置為1。二、相關(guān)知識fromthreadingimportThread,RLock

importtime

num=0

r_lock=RLock()

classMyThread(Thread):

defrun(self):

globalnum

time.sleep(1)

ifr_lock.acquire():

num=num+1

msg=+'將num改為'+str(num)

print(msg)

r_lock.acquire()

r_lock.release()

r_lock.release()if__name__=='__main__':

foriinrange(5):

t=MyThread()

t.start()可重入鎖同樣可以解決多線程訪問共享數(shù)據(jù)的沖突問題二、相關(guān)知識(4)通過Condition類實現(xiàn)線程同步線程按預(yù)定的次序執(zhí)行稱為線程的同步。例如,有兩個線程A和B,A負責從網(wǎng)絡(luò)上讀取數(shù)據(jù),并將數(shù)據(jù)保存到變量X中,B負責處理變量X中的數(shù)據(jù)。這時線程B就需要和A同步,也就是說B需要等A給它一個信號,它才可以開始去做自己的事情。同樣,B完成任務(wù)后也需要通知A,告訴A變量X中的數(shù)據(jù)已經(jīng)處理完了,可以將新的數(shù)據(jù)放入X了。二、相關(guān)知識Condition類代表條件變量,它允許線程在觸發(fā)某些事件或達到特定條件后才開始執(zhí)行。通過Condition類提供的構(gòu)造方法可以創(chuàng)建一個實例Condition(lock=None

)以上構(gòu)造方法中只有一個lock參數(shù),該參數(shù)用于接收一個Lock對象或RLock對象。若沒有為lock參數(shù)傳入值,Condition對象會自動生成一個RLock對象。二、相關(guān)知識Condition類代表條件變量,條件變量提供兩個接口,一個是用于等待線程,另一個是用于喚醒處于等待狀態(tài)的線程,涉及三個方法:二、相關(guān)知識二、相關(guān)知識importtimeimportthreading #導入threading庫def

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
  • 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

最新文檔

評論

0/150

提交評論