線程、進程、隊列、IO多路模型

操做系統工做原理介紹、線程、進程演化史、特色、區別、互斥鎖、信號、事件、join、GIL、進程間通訊、管道、隊列。
生產者消息者模型、異步模型、IO多路複用模型、select\poll\epoll 高性能IO模型源碼實例解析、高併發FTP server開發
```html

1、問答題

一、簡述計算機操做系統中的「中斷」的做用?python

cpu會切:io阻塞、程序運行時間過長
使計算機能夠更好更快利用有限的系統資源解決系統響應速度和運行效率的一種控制技術。
實時響應 + 系統調用

 


二、簡述計算機內存中的「內核態」和「用戶態」;linux

操做系統由操做系統的內核(運行於內核態,管理硬件資源)以及
系統調用(運行於用戶態,爲應用程序員寫的應用程序提供系統調用接口)兩部分組成;

內核態:cpu能夠訪問內存的全部數據,包括外圍設備,例如硬盤,網卡,cpu也能夠將本身從一個程序切換到另外一個程序。
用戶態:只能受限的訪問內存,且不容許訪問外圍設備,佔用cpu的能力被剝奪,cpu資源能夠被其餘程序獲取。

 

 

三、什麼是進程?
進程:正在進行的一個過程或者說一個任務。而負責執行任務則是cpu。程序員


四、什麼是線程?
線程顧名思義,就是一條流水線工做的過程(流水線的工做須要電源,電源就至關於cpu),而一條流水線必須屬於一個車間,
一個車間的工做過程是一個進程,車間負責把資源整合到一塊兒,是一個資源單位,而一個車間內至少有一條流水線。
因此,進程只是用來把資源集中到一塊兒(進程只是一個資源單位,或者說資源集合),而線程纔是cpu上的執行單位。web


五、簡述程序的執行過程;編程

1.激活了python的解釋器,有一個解釋器級別的垃圾回收線程(GIL鎖)。
2.一個進程下的多個線程去訪問解釋器的代碼,拿到執行權限,將程序看成參數傳遞給解釋器的代碼去執行。
3.保護不一樣的數據應該用不一樣的鎖。
4.python程序是順序執行的!
5.一段python程序以.py文件運行時,文件屬性__name__==__main__;做爲模塊導入時,文件屬性__name__爲文件名。

 


六、什麼是「系統調用」?windows

全部用戶程序都是運行在用戶態的,可是有時候程序確實須要作一些內核態的事情,例如從硬盤讀取數據,或者從鍵盤獲取輸入等,
而惟一能作這些事情的就是操做系統,因此此時程序就須要向操做系統請求以程序的名義來執行這些操做。
這時,就須要一個機制:用戶態程序切換到內核態,可是不能控制在內核態中執行的指令。這種機制就叫系統調用。

 

 

七、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。



八、進程間通訊方式有哪些?
管道、信號量、信號、消息隊列、共享內存、套接字服務器


九、簡述你對管道、隊列的理解;網絡

管道一般指無名管道
    一、它是半雙工的(即數據只能在一個方向上流動),具備固定的讀端和寫端
    二、它只能用於具備親緣關係的進程中通訊(也就是父與子進程或者兄弟進程之間)
    三、數據不可反覆讀取了,即讀了以後歡喜紅區中就沒有了
  消息隊列
    一、消息隊列是面向記錄的,其中的消息具備特定的格式以及特定的優先級
    二、消息隊列獨立於發送與接收進程。進程終止時,消息隊列及其內容不會被刪除。
    三、消息隊列能夠實現消息隨機查詢。

  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() 

 

十、請簡述你對join、daemon方法的理解,舉出它們在生產環境中的使用場景;

join: 等待一個任務執行完畢;能夠將併發變成串行。
daemon: 
守護進程(守護線程)會等待主進程(主線程)運行完畢後被銷燬。
運行完畢:
1.對主進程來講,運行完畢指的是主進程代碼運行完畢。
2.對主線程來講,運行完畢指的是主線程所在的進程內全部非守護線程通通運行完畢,主線程纔算運行完畢。

 

十一、請簡述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的優點並非對於單個鏈接能處理得更快,而是在於能處理更多的鏈接。

 

 

十二、threading中Lock和RLock的相同點和不一樣點;

Lock():互斥鎖,只能被acquire一次,可能會發生死鎖狀況。 
RLock():遞歸鎖,能夠連續acquire屢次。
RLock = Lock + counter
counter:記錄了acquire的次數,直到一個線程全部的acquire都被release,其餘線程才能得到資源。

 


1三、什麼是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的接口實現具備較好跨平臺能力的服務器會比較困難。
其次,該模型將事件探測和事件響應夾雜在一塊兒,一旦事件響應的執行體龐大,則對整個模型是災難性的。

 

 

1四、什麼是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操做系統

 


1五、簡述select和epoll的區別;

select: 調用select()時
  一、上下文切換轉換爲內核態
  二、將fd從用戶空間複製到內核空間
  三、內核遍歷全部fd,查看其對應事件是否發生
  四、若是沒發生,將進程阻塞,當設備驅動產生中斷或者timeout時間後,將進程喚醒,再次進行遍歷
  五、返回遍歷後的fd
  六、將fd從內核空間複製到用戶空間
select: 缺點
一、當文件描述符過多時,文件描述符在用戶空間與內核空間進行copy會很費時
  二、當文件描述符過多時,內核對文件描述符的遍歷也很浪費時間
  三、select最大僅僅支持1024個文件描述符
epoll很好的改進了select:
  一、epoll的解決方案在epoll_ctl函數中。每次註冊新的事件到epoll句柄中時,會把全部的fd拷貝進內核,而不是在epoll_wait的時候重複拷貝。epoll保證了每一個fd在整個過程當中只會拷貝一次。
  二、epoll會在epoll_ctl時把指定的fd遍歷一遍(這一遍必不可少)併爲每一個fd指定一個回調函數,當設備就緒,喚醒等待隊列上的等待者時,就會調用這個回調函數,而這個回調函數會把就緒的fd加入一個就緒鏈表。
epoll_wait的工做實際上就是在這個就緒鏈表中查看有沒有就緒的fd。
  三、epoll對文件描述符沒有額外限制。

 


1六、簡述多線程和多進程的使用場景;
多進程用於計算密集型,如金融分析;利用多核實現併發。
多線程用於IO密集型,如socket,爬蟲,web。


1七、請分別簡述threading.Condition、threading.event、threading.semaphore、的使用場景;

condition: 某些事件觸發或達到特定的條件後才處理數據。
event: 用來通知線程有一些事情已發生,從而啓動後繼任務的開始。
semaphore: 爲控制一個具備有限數量用戶資源而設計。

 

 

1八、假設有一個名爲threading_test.py的程序裏有一個li = [1, 2, 3, 4]的列表,另有a,b兩個函數分別往該列表中增長元素,
a函數須要修改li以前須要得到threading.Lock對象,b函數不須要,請問當線程t1執行a函數獲取到Lock對象以後並無release該對象的狀況下,
線程t2執行b函是否能夠修改li,爲何?

能夠,線程的數據是共享的,a 函數雖然上了鎖,沒有釋放。因爲b 函數不須要上鎖,就能夠訪問資源。

 

 

1九、簡述你對Python GIL的理解;

GIL(global interpreter lock)全局解釋器鎖
GIL是CPython的一個概念,本質是一把互斥鎖,將併發運行變成串行。
解釋器的代碼是全部線程共享的,因此垃圾回收線程也有可能訪問到解釋器的代碼去執行。
所以須要有GIL鎖,保證python解釋器同一時間只能執行一個任務的代碼。
GIL:解釋器級別的鎖(保護的是解釋器級別的數據,好比垃圾回收的數據)
Lock:應用程序的鎖(保護用戶本身開發的應用程序的數據)

 


20、請列舉你知道的進程間通訊方式;
消息隊列 管道 信號量 信號 共享內存 套接字


2一、什麼是同步I/O,什麼是異步I/O?

同步I/O,用戶進程須要主動讀寫數據。
異步I/O,不須要主動讀寫數據,只須要讀寫數據完成的通知。

 

2二、什麼是管道,若是兩個進程嘗試從管道的同一端讀寫數據,會出現什麼狀況?

管道:是兩個進程間進行單向通訊的機制。因爲管道傳遞數據的單向性。管道又稱爲半雙工管道。
管道傳遞數據是單向性的,讀數據時,寫入管道應關閉。寫數據時,讀取管道應關閉。

 

2三、爲何要使用線程池/進程池?
對服務端開啓的進程數或線程數加以控制,讓機器在一個本身能夠承受的範圍內運行,這就是進程池或線程池的用途.


2四、若是多個線程都在等待同一個鎖被釋放,請問當該鎖對象被釋放的時候,哪個線程將會得到該鎖對象?
這個由操做系統的調度決定。

2五、import threading;s = threading.Semaphore(value=-1)會出現什麼狀況?

當threading.Semaphore(1) 爲1時,表示只有一個線程可以拿到許可,其餘線程都處於阻塞狀態,直到該線程釋放爲止。
固然信號量不可能永久的阻塞在那裏。信號量也提供了超時處理機制。若是傳入了 -1,則表示無限期的等待。

 

 

2六、請將二進制數10001001轉化爲十進制;
10001001 = 1*10^7 + 1*10^3 + 1* 10^0 = 10001001


2七、某進程在運行過程當中須要等待從磁盤上讀入數據,此時該進程的狀態將發生什麼變化?

一個程序有三種狀態:運行態,阻塞態,就緒態;
遇到IO阻塞,進程從運行態轉到阻塞態,cpu切走,保存當前狀態;

 


2八、請問selectors模塊中DefaultSelector類的做用是什麼;

IO多路複用:select poll epoll
select: 列表循環,效率低。windows 支持。
poll: 可接收的列表數據多,效率也不高。linux 支持。
epoll: 效率最高 異步操做 + 回調函數。linux 支持。
selectors 模塊:
sel=selectors.DefaultSelector()
自動根據操做系統選擇select/poll/epoll

 

2九、簡述異步I/O的原理;

用戶進程發起read操做以後,馬上就能夠開始去作其它的事。而另外一方面,從kernel的角度,當它受到一個asynchronous read以後,
首先它會馬上返回,因此不會對用戶進程產生任何block。而後,kernel會等待數據準備完成,而後將數據拷貝到用戶內存,
當這一切都完成以後,kernel會給用戶進程發送一個signal,告訴它read操做完成了。

 


30、請問multiprocessing模塊中的Value、Array類的做用是什麼?舉例說明它們的使用場景

一般,進程之間彼此是徹底孤立的,惟一的通訊方式是隊列或管道。但可使用兩個對象來表示共享數據。
其實,這些對象使用了共享內存(經過mmap模塊)使訪問多個進程成爲可能。
python 多進程通訊Queue Pipe Value Array
queue和pipe用來在進程間傳遞消息;
Value + Array 是python中共享內存映射文件的方法;速度比較快.

 

3一、請問multiprocessing模塊中的Manager類的做用是什麼?與Value和Array類相比,Manager的優缺點是什麼?

Python multiprocessing.Manager(進程間共享數據):Python中進程間共享數據,除了基本的queue,pipe和value+array外,
還提供了更高層次的封裝,使用multiprocessing.Manager能夠簡單地使用這些高級接口。
Manager支持的類型有list,dict,Namespace,Lock,RLock,Semaphore,BoundedSemaphore,Condition,Event,Queue,Value和Array。

 



3二、請說說你對multiprocessing模塊中的Queue().put(), Queue.put_nowait(), Queue.get(), Queue.get_nowait()的理解;

q = Queue(3) 隊列 先進先出 進程間通訊; 隊列 = 管道 + 鎖
q.put() 
q.put_nowait() # 無阻塞,當隊列滿時,直接拋出異常queue.Full
q.get() 
q.get_nowait() # 無阻塞,當隊列爲空時,直接拋出異常queue.Empty

 

3三、什麼是協程?使用協程與使用線程的區別是什麼?

協程:單線程下的併發。協程是一種用戶態的輕量級線程,即協程是由用戶程序本身控制調度的。
1.python的線程是屬於內核級別的,即由操做系統控制調度(如單線程遇到io或執行時間過長就會被迫交出cpu執行權限,
切換其餘的線程運行)
2.單線程內開啓協程,一旦遇到io,就會從應用程序級別(而非操做系統)控制切換,以此來提高效率
(!!非io操做的切換與效率無關)

 


3四、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的實現會負責把數據從內核拷貝到用戶空間。

2、編程題

一、請寫一個包含10個線程的程序,主線程必須等待每個子線程執行完成以後才結束執行,每個子線程執行的時候都須要打印當前線程名、當前活躍線程數量以及當前線程名稱;

from threading import Thread, currentThread, activeCount
import time
def task(n):
    print(‘線程名:%s   %s‘%(currentThread(), n ))
    time.sleep(2)
    print(‘當前活躍線程數量:%s‘%activeCount())
if __name__ == ‘__main__‘:
    t_li = []
    for i in range(10):
        t = Thread(target=task, args=(i, ))
        t.start()
        t_li.append(t)
    for t in t_li:
        t.join()
    print(‘主,---end---‘)

打印:
線程名:<Thread(Thread-1, started 6212)>   0
線程名:<Thread(Thread-2, started 780)>   1
線程名:<Thread(Thread-3, started 6616)>   2
線程名:<Thread(Thread-4, started 5344)>   3
線程名:<Thread(Thread-5, started 6352)>   4
線程名:<Thread(Thread-6, started 6724)>   5
線程名:<Thread(Thread-7, started 1168)>   6
線程名:<Thread(Thread-8, started 1540)>   7
線程名:<Thread(Thread-9, started 6812)>   8
線程名:<Thread(Thread-10, started 6824)>   9
當前活躍線程數量:11
當前活躍線程數量:10
當前活躍線程數量:9
當前活躍線程數量:8
當前活躍線程數量:7
當前活躍線程數量:6
當前活躍線程數量:5
當前活躍線程數量:4
當前活躍線程數量:4
當前活躍線程數量:4
主,---end---

 

 

二、請寫一個包含10個線程的程序,並給每個子線程都建立名爲"name"的線程私有變量,變量值爲「Alex」;

from threading import Thread
def task(name):
    print(‘%s is running‘%name)
    #print(‘end---‘)
if __name__ == ‘__main__‘:
    for i in range(10):
        t = Thread(target=task, args=(‘alex_%s‘%i,) )
        t.start()
    print(‘----主-----‘)


打印:
alex_0 is running
alex_1 is running
alex_2 is running
alex_3 is running
alex_4 is running
alex_5 is running
alex_6 is running
alex_7 is running
alex_8 is running
alex_9 is running
----主-----

 

三、請使用協程寫一個消費者生產者模型;

def consumer():
    while True:
        x = yield
        print(‘消費:‘, x)
def producter():
    c = consumer()
    next(c)
    for i in range(10):
        print(‘生產:‘, i)
        c.send(i)
producter()

打印:
生產: 0
消費: 0
生產: 1
消費: 1
生產: 2
消費: 2
生產: 3
消費: 3
生產: 4
消費: 4
生產: 5
消費: 5
生產: 6
消費: 6
生產: 7
消費: 7
生產: 8
消費: 8
生產: 9
消費: 9

 


四、寫一個程序,包含十個線程,子線程必須等待主線程sleep 10秒鐘以後才執行,並打印當前時間;

from threading import Thread,Event
import time
import datetime
def task():
    event.wait(10)i
    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()

打印:
time: 2018-05-01 17:31:15.896462
time: 2018-05-01 17:31:15.897462
time: 2018-05-01 17:31:15.897462
time: 2018-05-01 17:31:15.897462
time: 2018-05-01 17:31:15.897462
time: 2018-05-01 17:31:15.897462
time: 2018-05-01 17:31:15.900462
time: 2018-05-01 17:31:15.900462
time: 2018-05-01 17:31:15.901462
time: 2018-05-01 17:31:15.901462

 

五、寫一個程序,包含十個線程,同時只能有五個子線程並行執行;

from threading import Thread, Semaphore, currentThread
import time
def task(n):
    sm.acquire()
    print(‘%s--‘%n,currentThread().name  )
    time.sleep(1)
    print(‘end‘)
    sm.release()

if __name__ == ‘__main__‘:
    sm = Semaphore(5)
    for i in range(10):
        t = Thread(target=task, args=(i, ))
        t.start()
打印:
0-- Thread-1
1-- Thread-2
2-- Thread-3
3-- Thread-4
4-- Thread-5
end
5-- Thread-6
end
end
end
6-- Thread-7
end
7-- Thread-8
8-- Thread-9
9-- Thread-10
end
end
end
end
end

 

 

六、寫一個程序 ,包含一個名爲hello的函數,函數的功能是打印字符串「Hello, World!」,該函數必須在程序執行30秒以後纔開始執行(不能使用time.sleep());

from threading import Timer
def hello(name):
    print(‘%s say ‘%name, ‘Hello World!‘)

if __name__ == ‘__main__‘:
    t = Timer(5, hello, args=(‘alex‘, ))
    t.start()
    
打印:
alex say  Hello World!

 


七、寫一個程序,利用queue實現進程間通訊;

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(‘主‘)
打印:
生產: 0
生產: 0
生產: 1
生產: 1
生產: 2
消費了: 1 --- Process-4
生產: 2
消費了: 1 --- Process-4
生產: 3
消費了: 2 --- Process-4
消費了: 2 --- Process-4
生產: 3
生產: 4
消費了: 3 --- Process-4
生產: 4
消費了: 3 --- Process-4
消費了: 4 --- Process-4
消費了: 4 --- Process-4
主

 

 

八、寫一個程序,利用pipe實現進程間通訊;

from multiprocessing import Process, Pipe
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())
打印:
hello world

 

 

九、使用selectors模塊建立一個處理客戶端消息的服務器程序;

#server
import socket
import selectors
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(‘closeing...‘,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)
        
##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‘))

 

十、使用socketserver建立服務器程序時,若是使用fork或者線程服務器,一個潛在的問題是,惡意的程序可能會發送大量的請求致使服務器崩潰,請寫一個程序,避免此類問題;

# server socketserver 模塊內部使用IO多路複用 和多進程/多線程
import socketserver
class Handler(socketserver.BaseRequestHandler):
    def handle(self):
        print(‘new connection:‘,self.client_address)
        while True:
            try:
                data = self.request.recv(1024)
                if not data:break
                print(‘client data:‘,data.decode())
                self.request.send(data.upper())
            except Exception as e:
                print(e)
                break

 

十一、請使用asyncio實現一個socket服務器端程序;

相關文章
相關標籤/搜索