Python程序設(shè)計 課件 第八章 多線程_第1頁
Python程序設(shè)計 課件 第八章 多線程_第2頁
Python程序設(shè)計 課件 第八章 多線程_第3頁
Python程序設(shè)計 課件 第八章 多線程_第4頁
Python程序設(shè)計 課件 第八章 多線程_第5頁
已閱讀5頁,還剩41頁未讀, 繼續(xù)免費閱讀

下載本文檔

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

文檔簡介

第八章

多線程Python程序設(shè)計01線程概述02使用Python實現(xiàn)多線程03綜合案例:基于多線程的爬蟲應(yīng)用04本章回顧01線程概述1.什么是進程進程是操作系統(tǒng)資源分配和調(diào)度的基本單位。計算機使用時多個同時運行的軟件程序,程序代碼存于磁盤,加載到內(nèi)存由CPU執(zhí)行,運行中可能與外設(shè)交互、產(chǎn)生數(shù)據(jù),操作系統(tǒng)將運行程序封裝成進程并按調(diào)度算法切換執(zhí)行,可按“Ctrl+Alt+Delete”打開任務(wù)管理器查看進程。01線程概述2.什么是線程在早期的操作系統(tǒng)中并沒有線程的概念,進程是擁有資源和獨立運行的最小單位,也是程序執(zhí)行的最小單位。后來隨著計算機行業(yè)的發(fā)展,程序功能的設(shè)計越來越復雜,而某些活動隨著時間的推移會被阻塞,此時就想到能否將這些應(yīng)用程序分解成更細粒度的實體,并且這些細粒度的執(zhí)行實體可以共享進程的地址空間,所以就出現(xiàn)了線程的概念。線程是操作系統(tǒng)能夠進行運算調(diào)度的最小單位,可以看做是輕量級的進程,線程之間切換的開銷小,所以線程的創(chuàng)建、銷毀、調(diào)度性能遠遠優(yōu)于進程。01線程概述3.進程和線程的區(qū)別進程和線程分工明確,進程有主線程,負責分配管理資源,線程負責CPU調(diào)度運算,是CPU切換時間片最小單位。一個進程多個線程,線程共享進程堆和方法區(qū)資源,各有自己的程序計數(shù)器和棧區(qū)域。01線程概述方法區(qū):用來存放加載的類、常量等信息。堆:是進程中最大的一塊內(nèi)存空間。棧:用于存儲該線程的局部變量(私有的)和棧禎。計數(shù)器:一塊內(nèi)存區(qū)域,用來記錄線程當前要執(zhí)行的指令地址。關(guān)于進程與線程的區(qū)別總結(jié)如右圖所示。多線程相關(guān)概念1.什么是多線程多線程是指程序中包含多個執(zhí)行流,即在一個程序中可以同時運行多個不同的線程來執(zhí)行不同的任務(wù)。舉一個生活中的例子,如果你吃完水果再看電視就是單線程,而一邊吃水果一邊看電視就是多線程。在擁有多核的機器上,使用多線程技術(shù),可以明顯提升系統(tǒng)的執(zhí)行效率。多線程相關(guān)概念2.并發(fā)與多線程多線程可以實現(xiàn)任務(wù)并發(fā)執(zhí)行,并發(fā)是指同一個時間段內(nèi)多個任務(wù)同時都在執(zhí)行,并且都沒有執(zhí)行結(jié)束,并發(fā)任務(wù)強調(diào)在一個時間段內(nèi)同時執(zhí)行,而一個時間段由多個單位時間累積而成,所以說并發(fā)的多個任務(wù)在單位時間內(nèi)不一定同時在執(zhí)行。多線程并發(fā)執(zhí)行過程中需要注意線程安全的問題,即當多個線程同時操作共享變量時,會出現(xiàn)某個線程更新共享變量的值,但是其它線程獲取到的是共享變量沒有被更新之前的值,這將導致數(shù)據(jù)不準確,可以使用多線程同步和加鎖解決此問題。多線程相關(guān)概念3.線程的生命周期當線程被創(chuàng)建并啟動以后,它既不是一啟動就進入執(zhí)行狀態(tài),也不是一直處于執(zhí)行狀態(tài),其要經(jīng)過新建(New)、就緒(Runnable)、運行(Running)、阻塞(Blocked)和死亡(Dead)5種狀態(tài),這5種狀態(tài)也叫線程的生命周期,如下圖所示。多線程相關(guān)概念(1)新建狀態(tài)當使用新建線程的方法建立一個線程后,該線程對象處于新建狀態(tài),注意此時線程未被啟動。(2)就緒狀態(tài)當處于新建狀態(tài)的線程,通過調(diào)用開始啟動線程方法進入到就緒狀態(tài),此時線程已經(jīng)被啟動,具備了運行條件,正在等待被分配CPU資源。簡單理解就是處于就緒狀態(tài)的線程已經(jīng)做好了運行準備,等獲取到CPU資源后就可以被運行。(3)運行狀態(tài)就緒態(tài)線程獲CPU資源進入運行態(tài)。單CPU時某時刻僅一個線程運行,多CPU時可多線程并行。運行線程按調(diào)度策略和優(yōu)先級調(diào)度,不會一直占CPU。運行態(tài)線程時間片內(nèi)正常結(jié)束進死亡態(tài),否則回就緒態(tài)。多線程相關(guān)概念(4)阻塞狀態(tài)當處于運行狀態(tài)的線程遇到某些情況就會進入到阻塞狀態(tài),如:調(diào)用了sleep()方法主動放棄其所占用的處理器資源。等待I/O流的輸入輸出。等待網(wǎng)絡(luò)資源。試圖獲得一個鎖對象,但該鎖對象正被其它線程所持有。等待某個通知。當引起阻塞的原因消除后,線程就會重新轉(zhuǎn)入到就緒狀態(tài)。(5)死亡狀態(tài)線程生命周期的最后一個階段就是死亡狀態(tài),進入死亡狀態(tài)的原因主要可總結(jié)如下:正常運行的線程完成了全部的工作。線程拋出未捕獲的異常。線程被強制性終止。需要注意的是主線程死亡,并不意味著所有線程全部死亡。也就是說,主線程的死亡,不會影響子線程繼續(xù)執(zhí)行,反之亦然。02使用Python實現(xiàn)多線程Python3的_thread庫和threading庫支持線程,重點介紹threading庫多線程實現(xiàn)。Python的GIL使同一時間僅一個線程能用CPU,其多線程雖不能提高CPU利用率,但能提高程序I/O(磁盤和網(wǎng)絡(luò)讀寫)訪問速度。使用threading實現(xiàn)多線程1.普通方式實現(xiàn)多線程接下來我們可以先定義2個函數(shù),然后模擬多線程的實現(xiàn)過程,如右圖所示。使用threading實現(xiàn)多線程在上面代碼中,函數(shù)study1()和study2()被稱為線程函數(shù),函數(shù)內(nèi)的代碼就是線程要執(zhí)行的內(nèi)容;在創(chuàng)建Thread類實例的時候,需要將線程函數(shù)的函數(shù)名傳給target參數(shù);線程實例創(chuàng)建完之后,需要使用start()方法啟動線程,這時study1()和study2()函數(shù)內(nèi)的代碼才會被執(zhí)行,運行結(jié)果如下圖所示。使用threading實現(xiàn)多線程從上圖中可以看出,多線程程序執(zhí)行順序不定。Thread類創(chuàng)建的是子線程,程序默認啟動主線程,主線程從程序首行、子線程從線程函數(shù)首行執(zhí)行,子線程啟動后有兩個線程。線程執(zhí)行時間由CPU調(diào)度定。執(zhí)行sleep()函數(shù)線程阻塞,結(jié)束后就緒等待調(diào)度,線程函數(shù)執(zhí)行順序不確定。使用threading實現(xiàn)多線程2.自定義線程自定義線程就是自己定義一個類,繼承自threading.Thread類,然后重寫其中的run()函數(shù),舉例如右圖所示。使用threading實現(xiàn)多線程在上述代碼中,我們定義了一個MyThread類繼承自threading.Thread類,然后重寫了構(gòu)造方法和run()方法。在main()函數(shù)中,我們對自定義的線程MyThread進行了實例化,然后使用其start()函數(shù)啟動線程,啟動之后就會運行子線程中的run()函數(shù),運行結(jié)果如右圖所示。使用threading實現(xiàn)多線程上頁代碼中的兩個線程是同時運行的,其運行結(jié)果很混亂,如果我們想讓一個先運行,一個后運行,可以使用join()方法實現(xiàn),其會一直等待對應(yīng)線程結(jié)束后才運行下一個線程,如右圖所示使用threading實現(xiàn)多線程從上圖中可以看出,其是按照順序執(zhí)行的,線程1完成所有工作之后,線程2才開始工作。默認情況下join()函數(shù)會一直等待對應(yīng)線程結(jié)束然后進入下一個線程,但是我們也可以通過其timeout參數(shù)設(shè)置指定時間(單位是秒),如“t.join(timeout=1)”。使用threading實現(xiàn)多線程3.threading.Thread類常用屬性和方法上面介紹了實現(xiàn)多線程的兩種方式,threading.Thread類中還有一些屬性和方法可以幫助我們獲取線程的一些信息,如獲取線程的名稱可以通過屬性name和getName(),如右圖所示。使用threading實現(xiàn)多線程為了方便大家記憶,現(xiàn)將threading.Thread類的常用屬性和方法總結(jié)如下表所示。線程鎖多線程開發(fā)特性:原子性:保證數(shù)據(jù)一致、解決線程安全問題,操作要么全執(zhí)行不被打斷,要么不執(zhí)行。可見性:多線程訪問同一變量,一個線程修改值,其他線程能馬上看到。有序性:程序按代碼先后順序執(zhí)行線程鎖但有時在使用多線程時,會違背其中的特性,接下來我們定義一個制作桌子的線程,然后啟動3個線程來工作,如下圖所示。線程鎖上述代碼運行結(jié)果從左圖中可以看出,需20個桌子,但運行結(jié)果序號超20(違背多線程特性),因3線程同時工作,一線程改值其他線程不能及時知曉。可借助線程鎖機制,鎖制作椅子和打印結(jié)果,避免CPU使用權(quán)被系統(tǒng)切換線程鎖2.線程鎖的使用threading模塊提供的Lock()函數(shù)可以提供線程鎖服務(wù),一旦線程獲得鎖,其他試圖獲取鎖的線程將被阻塞等待。給線程加鎖主要分為以下3步:①使用threading.Lock()初始化鎖。②使用Lock.acquire()獲取鎖。③使用Lock.rease()釋放鎖。線程鎖接下來對“做桌子”代碼實現(xiàn)加鎖服務(wù),如下圖所示。線程鎖加鎖之后的運行結(jié)果如右圖所示(不會再出現(xiàn)圖8-8中超出序號的情況)。案例【案例描述】無論是在工作中還是學習過程中,經(jīng)常需要對文件或文件夾進行復制,如從朋友那里復制電影、歌曲、小說等;從同事那里復制項目資源等。本案例我們分別使用單線程和多線程進行視頻文件夾的復制,并對比其效率?!景咐蟆吭贒盤中有一個文件夾videos,文件夾中是一些視頻文件,如右圖所示。請分別使用單線程和多線程的方式將其備份到D盤下的videosbak1和videosbak2文件夾下(文件夾已存在),并計算耗時。案例【代碼1】以單線程的方式復制文件【代碼2】以多線程的方式復制文件案例【運行結(jié)果】以單線程和多線程的方式復制文件的運行結(jié)果對比如圖1所示。從圖1中可以看出,以多線程的方式復制文件的運行效率是優(yōu)于單線程的,說明在進行I/O文件訪問時,多線程方案還是能提高程序的執(zhí)行效率的。這里由于文件比較少,對比結(jié)果不是特別明顯,如果處理大量文件時對比會更明顯。03綜合案例項目分析:項目背景:微信、QQ等社交軟件興起,網(wǎng)絡(luò)聊天成生活一部分,年輕人愛用表情包,其省事便利,能降成本、調(diào)氛圍、更好表達想法,“斗圖”時還考驗表情包庫存。項目需求:人們希望短時間獲海量表情包,常使用網(wǎng)絡(luò)爬蟲,Python類庫豐富,是爬蟲首選工具,本項目爬取一個表情包網(wǎng)站(下圖)。運行程序后,預(yù)期效果是電腦中會出現(xiàn)一個文件夾,文件夾中是下載后的表情包,并且表情包以其在網(wǎng)站中的標題命名。03綜合案例項目實現(xiàn)思路:爬蟲分網(wǎng)頁下載、解析、數(shù)據(jù)保存三階段,本項目適用。(1)網(wǎng)頁下載:分析網(wǎng)頁網(wǎng)址變化規(guī)律,此網(wǎng)站頁碼動態(tài)拼接在url結(jié)尾,主要用6.3.2介紹過的requests庫。(2)網(wǎng)頁解析:對下載得到的html內(nèi)容解析,找表情包標題、鏈接等數(shù)據(jù),主要用6.3.2的BeautifulSoup庫。(3)數(shù)據(jù)保存:通過解析獲取圖片url后,借瀏覽器request請求獲圖片二進制內(nèi)容,用file對象寫入保存。此外,本案例還用單線程和多線程爬蟲并對比耗時。單線程爬取表情包【核心知識點】①函數(shù)定義與調(diào)用;②Requests庫使用;③BeautifulSoup庫使用;④os、random等模塊使用?!竞诵牧鞒獭竣僦付ù鎴D文件夾,無則創(chuàng)建;②指定待爬取網(wǎng)頁url;③設(shè)多個瀏覽器User-Agent列表,模擬隨機選一個獲取headers;④用獲取的headers和requests庫獲取html內(nèi)容;⑤用BeautifulSoup庫依html內(nèi)容獲取img標簽的標題和圖片鏈接信息;⑥根據(jù)圖片鏈接信息獲取二進制內(nèi)容,以標題命名后保存到指定文件夾(循環(huán)保存)單線程爬取表情包單線程爬取表情包運行代碼之后,發(fā)現(xiàn)E盤多了一個名為“bqb”的文件夾,如下圖所示。多線程爬取表情包使用多線程實現(xiàn)爬蟲的流程與單線程基本類似,只是需要借助threading模塊和queue隊列模塊,關(guān)于隊列我們以前沒有接觸過,這里簡單介紹一下。1.queue模塊介紹在Python中,多個線程之間的數(shù)據(jù)是共享的,多個線程進行數(shù)據(jù)交換的時候,為了能確保數(shù)據(jù)的安全性和一致性,就可以使用隊列。Python中的queue模塊實現(xiàn)了隊列,其提供了同步的、線程安全的隊列類,包括先入先出隊列(Queue)、后入先出隊列(LifoQueue)和優(yōu)先級隊列(PriorityQueue)。queue模塊的常用方法可總結(jié)如下表所示。多線程爬取表情包多線程爬取表情包其中使用get([block[,timeout]])方法從隊列中獲取任務(wù),主要流程如下:①嘗試獲取互斥鎖。②如果此時隊列為空,則wait等待生產(chǎn)者線程添加數(shù)據(jù)。③get到任務(wù)后,會調(diào)用self.not_full.notify()通知生產(chǎn)者線程,隊列可以添加元素了。④釋放互斥鎖。使用put(item)向隊列中添加任務(wù),主要流程如下:①申請獲得互斥鎖。②獲得后,如果隊列未滿,則向隊列中添加數(shù)據(jù),并通知notify其它阻塞的某個線程,喚醒等待獲取require互斥鎖。③如果隊列已滿,則會wait等待,最后處理完成后釋放互斥鎖。多線程爬取表情包2案例實現(xiàn)【核心知識點】①函數(shù)的定義與調(diào)用。②Requests庫的使用。③BeautifulSoup庫的使用。④os、random等模塊的使用。⑤threading線程模塊的使用。⑥queue隊列模塊的使用。同樣是下載3頁表情包,單線程耗時186秒,而多線程只需要7秒,效率較單線程高了很多。如右圖所示。多線程爬取表情包多線程爬取表情包多線程爬取表情包上述代碼的運行結(jié)果如下圖所示。本章回顧1.【多選】以下關(guān)于進程和線程的描述正確的有()。A.進程是操作系統(tǒng)資源分配的基本單位B.線程是CPU調(diào)度的最小單位C.每個進程都有獨立的內(nèi)存空間D.同一進程中的線程可以共享內(nèi)存空間2.【多選】以下關(guān)于線程的生命周期描述正確的

溫馨提示

  • 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

提交評論