1. 程式人生 > >第八章網絡編程進階

第八章網絡編程進階

這就是 unix 結束 get 互斥 system 修改 .net 否則

####一、問答題

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服務器端程序;

第八章網絡編程進階