第八章網路程式設計進階
阿新 • • 發佈:2018-11-26
####一、問答題
1、簡述計算機作業系統中的“中斷”的作用?
中斷指當出現需要時,CPU暫時停止當前程式的執行轉而執行處理新情況的程式和執行過程 計算機執行期間,系統內發生任何非尋常的或非預期的急需處理事件,使得cpu暫時中斷當前正在執行的程式而轉去執行相應的事件處理程式。 待處理完畢後又返回原來被中斷處繼續執行或排程新的程序執行的過程。它使計算機可以更好更快利用有限的系統資源解決系統響應速度和執行效率的一種控制技術。 實時響應 + 系統呼叫
2、簡述計算機記憶體中的“核心態”和“使用者態”;
作業系統由作業系統的核心(運行於核心態,管理硬體資源)以及 系統呼叫(運行於使用者態,為應用程式設計師寫的應用程式提供系統呼叫介面)兩部分組成; 核心態:cpu可以訪問記憶體的所有資料,包括外圍裝置,例如硬碟,網絡卡,cpu也可以將自己從一個程式切換到另一個程式。 使用者態:只能受限的訪問記憶體,且不允許訪問外圍裝置,佔用cpu的能力被剝奪,cpu資源可以被其他程式獲取。 使用者態的應用程式可以通過三種方式來訪問核心態的資源: 1)系統呼叫 2)庫函式 3)Shell指令碼 使用者態到核心態的切換: 1.系統呼叫 使用者程式主動發起的 軟中斷 os.fork() process 2.異常 被動的 當CPU正在執行執行在使用者態的程式時,突然發生某些預先不可知的異常事件,這個時候就會觸發從當前使用者態執行的程序 轉向核心態執行相關的異常事件,典型的如缺頁異常。 3.外圍裝置的硬中斷 被動的 外圍裝置完成使用者的請求操作後,會像CPU發出中斷訊號,此時,CPU就會暫停執行下一條即將要執行的指令, 轉而去執行中斷訊號對應的處理程式,如果先前執行的指令是在使用者態下,則自然就發生從使用者態到核心態的轉換。
3、什麼是程序?
程序:正在進行的一個過程或者說一個任務。而負責執行任務則是cpu。
4、什麼是執行緒?
執行緒顧名思義,就是一條流水線工作的過程(流水線的工作需要電源,電源就相當於cpu),而一條流水線必須屬於一個車間, 一個車間的工作過程是一個程序,車間負責把資源整合到一起,是一個資源單位,而一個車間內至少有一條流水線。 所以,程序只是用來把資源集中到一起(程序只是一個資源單位,或者說資源集合),而執行緒才是cpu上的執行單位。
5、簡述程式的執行過程;
1.激活了python的直譯器,有一個直譯器級別的垃圾回收執行緒(GIL鎖)。 2.一個程序下的多個執行緒去訪問直譯器的程式碼,拿到執行許可權,將程式當作引數傳遞給直譯器的程式碼去執行。 3.保護不同的資料應該用不同的鎖。 4.python程式是順序執行的! 5.一段python程式以.py檔案執行時,檔案屬性__name__==__main__;作為模組匯入時,檔案屬性__name__為檔名。
6、什麼是“系統呼叫”?
所有使用者程式都是執行在使用者態的,但是有時候程式確實需要做一些核心態的事情,例如從硬碟讀取資料,或者從鍵盤獲取輸入等, 而唯一能做這些事情的就是作業系統,所以此時程式就需要向作業系統請求以程式的名義來執行這些操作。 這時,就需要一個機制:使用者態程式切換到核心態,但是不能控制在核心態中執行的指令。這種機制就叫系統呼叫。
7、threading模組event和condition的區別;
condition參考:https://blog.csdn.net/a349458532/article/details/51590040 https://blog.csdn.net/u013346751/article/details/78500412 condition: 某些事件觸發或達到特定的條件後才處理資料,預設建立了一個lock物件。 con = threading.Condition() con.acquire() con.notify() con.wait() con.release() event:其他執行緒需要通過判斷某個執行緒的狀態來確定自己的下一步操作,就可以用event。 from threading import Event event = Event() event.set(): 設定event的狀態值為True,所有阻塞池的執行緒啟用進入就緒狀態, 等待作業系統排程; event.is_set():返回event的狀態值; event.wait():如果 event.is_set()==False將阻塞執行緒; event.clear():恢復event的狀態值為False。
8、程序間通訊方式有哪些?
管道、訊號量、訊號、訊息佇列、共享記憶體、套接字 1)管道 管道分為有名管道和無名管道 無名管道是一種半雙工的通訊方式,資料只能單向流動,而且只能在具有親緣關係的程序間使用.程序的親緣關係一般指的是父子關係。無明管道一般用於兩個不同程序之間的通訊。 當一個程序建立了一個管道,並呼叫fork建立自己的一個子程序後,父程序關閉讀管道端,子程序關閉寫管道端,這樣提供了兩個程序之間資料流動的一種方式。 有名管道也是一種半雙工的通訊方式,但是它允許無親緣關係程序間的通訊。 2)訊號量 訊號量是一個計數器,可以用來控制多個執行緒對共享資源的訪問.,它不是用於交換大批資料,而用於多執行緒之間的同步.它常作為一種鎖機制,防止某程序在訪問資源時其它程序也訪問該資源. 因此,主要作為程序間以及同一個程序內不同執行緒之間的同步手段. 3)訊號 訊號是一種比較複雜的通訊方式,用於通知接收程序某個事件已經發生. 4)訊息佇列 訊息佇列是訊息的連結串列,存放在核心中並由訊息佇列識別符號標識.訊息佇列克服了訊號傳遞資訊少,管道只能承載無格式位元組流以及緩衝區大小受限等特點. 訊息佇列是UNIX下不同程序之間可實現共享資源的一種機制,UNIX允許不同程序將格式化的資料流以訊息佇列形式傳送給任意程序. 對訊息佇列具有操作許可權的程序都可以使用msget完成對訊息佇列的操作控制.通過使用訊息型別,程序可以按任何順序讀資訊,或為訊息安排優先順序順序. 5)共享記憶體 共享記憶體就是對映一段能被其他程序所訪問的記憶體,這段共享記憶體由一個程序建立,但多個程序都可以訪問.共享記憶體是最快的IPC(程序間通訊)方式, 它是針對其它程序間通訊方式執行效率低而專門設計的.它往往與其他通訊機制,如訊號量,配合使用,來實現程序間的同步與通訊. 6)套接字:可用於不同及其間的程序通訊
9、簡述你對管道、佇列的理解;
管道通常指無名管道 1、它是半雙工的(即資料只能在一個方向上流動),具有固定的讀端和寫端 2、它只能用於具有親緣關係的程序中通訊(也就是父與子程序或者兄弟程序之間) 3、資料不可反覆讀取了,即讀了之後歡喜紅區中就沒有了 訊息佇列 1、訊息佇列是面向記錄的,其中的訊息具有特定的格式以及特定的優先順序 2、訊息佇列獨立於傳送與接收程序。程序終止時,訊息佇列及其內容不會被刪除。 3、訊息佇列可以實現訊息隨機查詢。 mutiprocessing模組為我們提供的基於訊息的IPC通訊機制:佇列和管道。 佇列和管道都是將資料存放於記憶體中,而佇列又是基於(管道+鎖)實現的, 可以讓我們從複雜的鎖問題中解脫出來,因而佇列才是程序間通訊的最佳選擇。 我們應該儘量避免使用共享資料,儘可能使用訊息傳遞和佇列,避免處理複雜的同步和鎖問題, 而且在程序數目增多時,往往可以獲得更好的可展性。 佇列 = 管道 + 鎖 from multiprocessing import Queue,Process queue = Queue() queue.put(url) url = queue.get() from multiprocessing import Pipe,Process pipe = Pipe() pipe.send(url) pipe.recv()
10、請簡述你對join、daemon方法的理解,舉出它們在生產環境中的使用場景;
join: 等待一個任務執行完畢;可以將併發變成序列。 daemon: 守護程序(守護執行緒)會等待主程序(主執行緒)執行完畢後被銷燬。 執行完畢: 1.對主程序來說,執行完畢指的是主程序程式碼執行完畢。 2.對主執行緒來說,執行完畢指的是主執行緒所在的程序內所有非守護執行緒統統執行完畢,主執行緒才算執行完畢。
11、請簡述IO多路複用模型的工作原理
IO多路複用實際上就是用select,poll,epoll監聽多個io物件,當io物件有變化(有資料)的時候就通知使用者程序。好處就是單個程序可以處理多個socket。 1.當用戶程序呼叫了select,那麼整個程序會被block; 2.而同時,kernel會“監視”所有select負責的socket; 3.當任何一個socket中的資料準備好了,select就會返回; 4.這個時候使用者程序再呼叫read操作,將資料從kernel拷貝到使用者程序。 總結: 1.I/O 多路複用的特點是通過一種機制一個程序能同時等待多個檔案描述符,而這些檔案描述符(套接字描述符)其中的任意一個進入讀就緒狀態,select()函式就可以返回。 2.IO多路複用:需要兩個系統呼叫,system call (select 和 recvfrom),而blocking IO只調用了一個system call (recvfrom)。但是, 用select的優勢在於它可以同時處理多個connection。 3.如果處理的連線數不是很高的話,使用select/epoll的web server不一定比使用多執行緒 + 阻塞 IO的web server效能更好,可能延遲還更大。 4.select/epoll的優勢並不是對於單個連線能處理得更快,而是在於能處理更多的連線。
12、threading中Lock和RLock的相同點和不同點;
Lock():互斥鎖,只能被acquire一次,可能會發生死鎖情況。 RLock():遞迴鎖,可以連續acquire多次。 RLock = Lock + counter counter:記錄了acquire的次數,直到一個執行緒所有的acquire都被release,其他執行緒才能獲得資源。
*13、什麼是select,請簡述它的工作原理,簡述它的優缺點;
python中的select模組專注於I/O多路複用,提供了select poll epoll三個方法;後兩個在linux中可用,windows僅支援select。 fd:檔案描述符 fd_r_list,fd_w_list,fd_e_list = select.select(rlist,wlist,xlist,[timeout]) 引數:可接受四個引數(前三個必須) rlist:等到準備好閱讀 wlist:等到準備寫作 xlist:等待“異常情況” 超時:超時時間 返回值:三個列表 select監聽fd變化的過程分析: 使用者程序建立socket物件,拷貝監聽的fd到核心空間,每一個fd會對應一張系統檔案表,核心空間的fd響應到資料後, 就會發送訊號給使用者程序資料已到; 使用者程序再發送系統呼叫,比如(accept)將核心空間的資料copy到使用者空間,同時作為接受資料端核心空間的資料清除, 這樣重新監聽時fd再有新的資料又可以響應到了(傳送端因為基於TCP協議所以需要收到應答後才會清除)。 該模型的優點: 相比其他模型,使用select() 的事件驅動模型只用單執行緒(程序)執行,佔用資源少,不消耗太多 CPU,同時能夠為多客戶端提供服務。 如果試圖建立一個簡單的事件驅動的伺服器程式,這個模型有一定的參考價值。 該模型的缺點: 首先select()介面並不是實現“事件驅動”的最好選擇。因為當需要探測的控制代碼值較大時,select()介面本身需要消耗大量時間去輪詢各個控制代碼。 很多作業系統提供了更為高效的介面,如linux提供了epoll,BSD提供了kqueue,Solaris提供了/dev/poll,…。 如果需要實現更高效的伺服器程式,類似epoll這樣的介面更被推薦。遺憾的是不同的作業系統特供的epoll介面有很大差異, 所以使用類似於epoll的介面實現具有較好跨平臺能力的伺服器會比較困難。 其次,該模型將事件探測和事件響應夾雜在一起,一旦事件響應的執行體龐大,則對整個模型是災難性的。
*14、什麼是epoll,請簡述它的工作原理,簡述它的優缺點;
epoll: 效能最好的多路複用I/O就緒通知方法。相比於select,epoll最大的好處在於它不會隨著監聽fd數目的增長而降低效率。 因為在核心中的select實現中,它是採用輪詢來處理的,輪詢的fd數目越多,自然耗時越多。 epoll:同樣只告知那些就緒的檔案描述符,而且當我們呼叫epoll_wait()獲得就緒檔案描述符時,返回的不是實際的描述符,而是一個代表就緒描述符數量的值, 你只需要去epoll指定的一個數組中依次取得相應數量的檔案描述符即可,這裡也使用了記憶體對映(mmap)技術,這樣便徹底省掉了這些檔案描述符在系統呼叫時複製的開銷。 另一個本質的改進在於epoll採用基於事件的就緒通知方式。在select/poll中,程序只有在呼叫一定的方法後,核心才對所有監視的檔案描述符進行掃描, 而epoll事先通過epoll_ctl()來註冊一個檔案描述符,一旦基於某個檔案描述符就緒時,核心會採用類似callback的回撥機制,迅速啟用這個檔案描述符, 當程序呼叫epoll_wait()時便得到通知。從以上可知,epoll是對select、poll模型的改進,提高了網路程式設計的效能,廣泛應用於大規模併發請求的C/S架構中。 python中的epoll: 只適用於unix/linux作業系統
*15、簡述select和epoll的區別;
select: 呼叫select()時 1、上下文切換轉換為核心態 2、將fd從使用者空間複製到核心空間 3、核心遍歷所有fd,檢視其對應事件是否發生 4、如果沒發生,將程序阻塞,當裝置驅動產生中斷或者timeout時間後,將程序喚醒,再次進行遍歷 5、返回遍歷後的fd 6、將fd從核心空間複製到使用者空間 select: 缺點 1、當檔案描述符過多時,檔案描述符在使用者空間與核心空間進行copy會很費時 2、當檔案描述符過多時,核心對檔案描述符的遍歷也很浪費時間 3、select最大僅僅支援1024個檔案描述符 epoll很好的改進了select: 1、epoll的解決方案在epoll_ctl函式中。每次註冊新的事件到epoll控制代碼中時,會把所有的fd拷貝進核心,而不是在epoll_wait的時候重複拷貝。epoll保證了每個fd在整個過程中只會拷貝一次。 2、epoll會在epoll_ctl時把指定的fd遍歷一遍(這一遍必不可少)併為每個fd指定一個回撥函式,當裝置就緒,喚醒等待佇列上的等待者時,就會呼叫這個回撥函式,而這個回撥函式會把就緒的fd加入一個就緒連結串列。 epoll_wait的工作實際上就是在這個就緒連結串列中檢視有沒有就緒的fd。 3、epoll對檔案描述符沒有額外限制。
16、簡述多執行緒和多程序的使用場景;
多程序用於計算密集型,如金融分析;利用多核實現併發。 多執行緒用於IO密集型,如socket,爬蟲,web。
17、請分別簡述threading.Condition、threading.event、threading.semaphore、的使用場景;
condition: 某些事件觸發或達到特定的條件後才處理資料。 event: 用來通知執行緒有一些事情已發生,從而啟動後繼任務的開始。 semaphore: 為控制一個具有有限數量使用者資源而設計。
19、簡述你對Python GIL的理解;
GIL(global interpreter lock)全域性直譯器鎖 GIL是CPython的一個概念,本質是一把互斥鎖,將併發執行變成序列。 直譯器的程式碼是所有執行緒共享的,所以垃圾回收執行緒也有可能訪問到直譯器的程式碼去執行。 因此需要有GIL鎖,保證python直譯器同一時間只能執行一個任務的程式碼。 GIL:直譯器級別的鎖(保護的是直譯器級別的資料,比如垃圾回收的資料) Lock:應用程式的鎖(保護使用者自己開發的應用程式的資料)
20、請列舉你知道的程序間通訊方式;
訊息佇列 管道 訊號量 訊號 共享記憶體 套接字
21、什麼是同步I/O,什麼是非同步I/O?
同步I/O,使用者程序需要主動讀寫資料。 非同步I/O,不需要主動讀寫資料,只需要讀寫資料完成的通知。
22、什麼是管道,如果兩個程序嘗試從管道的同一端讀寫資料,會出現什麼情況?
管道:是兩個程序間進行單向通訊的機制。由於管道傳遞資料的單向性。管道又稱為半雙工管道。 管道傳遞資料是單向性的,讀資料時,寫入管道應關閉。寫資料時,讀取管道應關閉。
23、為什麼要使用執行緒池/程序池?
對服務端開啟的程序數或執行緒數加以控制,讓機器在一個自己可以承受的範圍內執行,這就是程序池或執行緒池的用途.
24、如果多個執行緒都在等待同一個鎖被釋放,請問當該鎖物件被釋放的時候,哪一個執行緒將會獲得該鎖物件?
這個由作業系統的排程決定。
25、import threading;s = threading.Semaphore(value=-1)會出現什麼情況?
threading.Semaphore(1) 為1時,表示只有一個執行緒能夠拿到許可,其他執行緒都處於阻塞狀態,直到該執行緒釋放為止。 當然訊號量不可能永久的阻塞在那裡。訊號量也提供了超時處理機制。如果傳入了 -1,則表示無限期的等待。
26、請將二進位制數10001001轉化為十進位制;
1 0 0 0 1 0 0 1 128 64 32 16 8 4 2 1
137
27、某程序在執行過程中需要等待從磁碟上讀入資料,此時該程序的狀態將發生什麼變化?
一個程式有三種狀態:執行態,阻塞態,就緒態; 遇到IO阻塞,程序從執行態轉到阻塞態,cpu切走,儲存當前狀態;
29、簡述非同步I/O的原理;
使用者程序發起read操作之後,立刻就可以開始去做其它的事。而另一方面,從kernel的角度,當它受到一個asynchronous read之後, 首先它會立刻返回,所以不會對使用者程序產生任何block。然後,kernel會等待資料準備完成,然後將資料拷貝到使用者記憶體, 當這一切都完成之後,kernel會給使用者程序傳送一個signal,告訴它read操作完成了。
30、請問multiprocessing模組中的Value、Array類的作用是什麼?舉例說明它們的使用場景
通常,程序之間彼此是完全孤立的,唯一的通訊方式是佇列或管道。但可以使用兩個物件來表示共享資料。其實,這些物件使用了共享記憶體(通過mmap模組)使訪問多個程序成為可能。
Value( typecode, arg1, … argN, lock ) 在共享內容中常見ctypes物件。typecode要麼是包含array模組使用的相同型別程式碼(如’i’,’d’等)的字串,要麼是來自ctypes模組的型別物件(如ctypes.c_int、ctypes.c_double等)。 所有額外的位置引數arg1, arg2 ….. argN將傳遞給指定型別的建構函式。lock是隻能使用關鍵字呼叫的引數,如果把它置為True(預設值),將建立一個新的鎖定來包含對值的訪問。 如果傳入一個現有鎖定,比如Lock或RLock例項,該鎖定將用於進行同步。如果v是Value建立的共享值的例項,便可使用v.value訪問底層的值。例如,讀取v.value將獲取值,而賦值v.value將修改值。 RawValue( typecode, arg1, … ,argN) 同Value物件,但不存在鎖定。 Array( typecode, initializer, lock ) 在共享記憶體中建立ctypes陣列。typecode描述了陣列的內容,意義與Value()函式中的相同。initializer要麼是設定陣列初始大小的整數,要麼是專案序列,其值和大小用於初始化陣列。lock是隻能使用關鍵字呼叫的引數,意義與Value()函式中相同。 如果a是Array建立的共享陣列的例項,便可使用標準的python索引、切片和迭代操作訪問它的內容,其中每種操作均由鎖定進行同步。對於位元組字串,a還具有a.value屬性,可以吧整個陣列當做一個字串進行訪問。 RawArray(typecode, initializer ) 同Array物件,但不存在鎖定。當所編寫的程式必須一次性操作大量的陣列項時,如果同時使用這種資料型別和用於同步的單獨鎖定(如果需要的話),效能將得到極大的提升。
31、請問multiprocessing模組中的Manager類的作用是什麼?與Value和Array類相比,Manager的優缺點是什麼?
可以通過使用Value或者Array把資料儲存在一個共享的記憶體表中;Manager()返回一個manager型別,控制一個server process,可以允許其它程序通過代理複製一些python objects 支援list,dict,Namespace,Lock,Semaphore,BoundedSemaphore,Condition,Event,Queue,Value,Array ; Manager類的作用共享資源,manger的的優點是可以在poor程序池中使用,缺點是windows下環境下效能比較差,因為windows平臺需要把Manager.list放在if name='main'下, 而在例項化子程序時,必須把Manager物件傳遞給子程序,否則lists無法被共享,而這個過程會消耗巨大資源,因此效能很差。 參考:http://www.kaka-ace.com/python-multiprocessing-module-2-managers-first-profile/ https://blog.csdn.net/alvine008/article/details/24310939
32、請說說你對multiprocessing模組中的Queue().put(), Queue.put_nowait()
q = Queue(3) 佇列 先進先出 程序間通訊; 佇列 = 管道 + 鎖 q.put() q.put_nowait() # 無阻塞,當佇列滿時,直接丟擲異常queue.Full q.get() q.get_nowait() # 無阻塞,當佇列為空時,直接丟擲異常queue.Empty
33、什麼是協程?使用協程與使用執行緒的區別是什麼?
協程:單執行緒下的併發。協程是一種使用者態的輕量級執行緒,即協程是由使用者程式自己控制排程的。 1.python的執行緒是屬於核心級別的,即由作業系統控制排程(如單執行緒遇到io或執行時間過長就會被迫交出cpu執行許可權, 切換其他的執行緒執行) 2.單執行緒內開啟協程,一旦遇到io,就會從應用程式級別(而非作業系統)控制切換,以此來提升效率 (!!非io操作的切換與效率無關)
34、asyncio的實現原理是什麼?
https://www.cnblogs.com/earendil/p/7411115.html Python非同步程式設計:asyncio庫和async/await語法 asyncio是Python 3.4 試驗性引入的非同步I/O框架,提供了基於協程做非同步I/O編寫單執行緒併發程式碼的基礎設施。 其核心元件有事件迴圈(Event Loop)、協程(Coroutine)、任務(Task)、未來物件(Future)以及其他一些擴充和輔助性質的模組。 synchronous io: 做”IO operation”的時候會將process阻塞;”IO operation”是指真實的IO操作 blocking IO,non-blocking IO,IO multiplexing都屬於synchronous IO這一類. asynchronous io: 當程序發起IO 操作之後,就直接返回再也不理睬了,直到kernel傳送一個訊號, 告訴程序說IO完成。在這整個過程中,程序完全沒有被block。非同步io的實現會負責把資料從核心拷貝到使用者空間。
#### 二、程式設計題
1、請寫一個包含10個執行緒的程式,主執行緒必須等待每一個子執行緒執行完成之後才結束執行,每一個子執行緒執行的時候都需要列印當前執行緒名、當前活躍執行緒數量以及當前執行緒名稱;
# -*- coding:utf-8 -*- from threading import Thread,currentThread,activeCount import time def task(n): print("執行緒名:\t%s\t%s" % (currentThread(),n)) time.sleep(2) print("當前活躍程序數量", activeCount()) if __name__ == '__main__': li = [] for i in range(10): t = Thread(target=task, args=(i,)) t.start() li.append(t) for t in li: t.join() print("主,---end---")View Code
2、請寫一個包含10個執行緒的程式,並給每一個子執行緒都建立名為"name"的執行緒私有變數,變數值為“Alex”;
# -*- coding:utf-8 -*- from threading import Thread def task(name): print('%s is running' % name) if __name__ == '__main__': for i in range(10): t = Thread(target=task, args=("alex_%s"%i,)) t.start() print("主")View Code
3、請使用協程寫一個消費者生產者模型;
# -*- coding:utf-8 -*- def consumer(): while True: x = yield print("消費", x) def product(): c = consumer() next(c) for i in range(10): print("生產", i) c.send(i) product()View Code
4、寫一個程式,包含十個執行緒,子執行緒必須等待主執行緒sleep 10秒鐘之後才執行,並列印當前時間;
# -*- coding:utf-8 -*- from threading import Thread,Event import time import datetime def task(): event.wait(10) print("time:", datetime.datetime.now()) if __name__ == '__main__': event = Event() for i in range(10): t = Thread(target=task) t.start() time.sleep(10) event.set()View Code
5、寫一個程式,包含十個執行緒,同時只能有五個子執行緒並行執行;
# -*- coding:utf-8 -*- from threading import Thread,Semaphore,currentThread import time sem = Semaphore(5) def task(): with sem: print("%s in" % currentThread().getName()) time.sleep(10) if __name__ == '__main__': for i in range(10): t = Thread(target=task,) t.start()View Code
6、寫一個程式 ,包含一個名為hello的函式,函式的功能是列印字串“Hello, World!”,該函式必須在程式執行30秒之後才開始執行(不能使用time.sleep());
# -*- coding:utf-8 -*- from threading import Timer def task(): print("Hello,World!") if __name__ == '__main__': t = Timer(30, task,) t.start()View Code
7、寫一個程式,利用queue實現程序間通訊;
# -*- coding:utf-8 -*- from multiprocessing import Process,current_process,Queue import time def consumer(q): while True: res = q.get() if not res:break print("消費了:", res, "---", current_process().name) def producter(q): for i in range(5): print("生產了", i) time.sleep(1) q.put(i) if __name__ == '__main__': q = Queue() p1 = Process(target=producter, args=(q,)) p2 = Process(target=producter, args=(q,)) c1 = Process(target=consumer, args=(q,)) c2 = Process(target=consumer, args=(q,)) c3 = Process(target=consumer, args=(q,)) p1.start() p2.start() c1.start() c2.start() c3.start() p1.join() p2.join() q.put(None) # None代表結束訊號,有幾個消費者來幾個訊號 q.put(None) # 在主程序裡邊確保所有的生產者都生產結束之後才髮結束訊號 q.put(None) print("主")View Code
8、寫一個程式,利用pipe實現程序間通訊;
# -*- coding:utf-8 -*- from multiprocessing import Pipe,Process def task(conn): conn.send('Hello,World!') conn.close() if __name__ == '__main__': parent_conn,child_conn = Pipe() p = Process(target=task, args=(child_conn,)) p.start() p.join() print(parent_conn.recv())View Code
9、使用selectors模組建立一個處理客戶端訊息的伺服器程式;
# server import selectors import socket sel = selectors.DefaultSelector() def accept(server_fileobj, mask): conn, addr = server_fileobj.accept() print(addr) sel.register(conn, selectors.EVENT_READ, read) def read(conn, mask): try: data = conn.recv(1024) if not data: print("closing....", conn) sel.unregister(conn) conn.close() return conn.send(data.upper()) except Exception: print("closing....", conn) sel.unregister(conn) conn.close() server_fileobj = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_fileobj.bind(("127.0.0.1", 8080)) server_fileobj.listen(5) server_fileobj.setblocking(False) sel.register(server_fileobj, selectors.EVENT_READ, accept) while True: events = sel.select() for sel_obj,mask in events: callback = sel_obj.data callback(sel_obj.fileobj, mask)View Code
#client import socket client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) client.connect(("127.0.0.1", 8080)) while True: msg = input(">>>:").strip() if not msg:continue client.send(msg.encode("utf-8")) data = client.recv(1024) print(data.decode("utf-8"))View Code
11、請使用asyncio實現一個socket伺服器端程式;