目錄python
Python本身沒有這玩意,Python中調用的操做系統的線程和進程。git
計算密集型操做:效率低,Python內置的一個全局解釋器鎖,鎖的做用就是保證同一時刻一個進程中只有一個線程能夠被cpu調度,多線程沒法利用多核優點,能夠經過多進程方式解決,可是比較浪費資源。
IO操做:效率高程序員
計算密集型操做:效率高(浪費資源),不得已而爲之。
IO操做:效率高(浪費資源)github
Python語言的創始人在開發這門語言時,目的快速把語言開發出來,若是加上GIL鎖(C語言加鎖),切換時按照100條字節指令來進行線程間的切換。web
GIL鎖,全局解釋器鎖。用於限制一個進程中同一時刻只有一個線程被cpu調度。
擴展:默認GIL鎖在執行100個cpu指令(過時時間)。
查看GIL切換的指令個數多線程
import sys v1 = sys。getcheckinterval() print(v1)
因爲線程是cpu工做的最小單元,建立線程能夠利用多核優點實現並行操做(Java/C#)。
注意:線程是爲了工做。併發
進程和進程之間作數據隔離(Java/C#)。app
注意:進程是爲了提供環境讓線程工做。框架
進程是資源分配的最小單位,線程是程序執行的最小單位。異步
進程有本身的獨立地址空間,每啓動一個進程,系統就會爲它分配地址空間,創建數據表來維護代碼段,堆棧段和數據段,這種操做很是昂貴。而線程是共享進程中的數據的,使用相同的地址空間,所以CPU切換一個線程的花費遠比進程小不少,同時建立一個線程的開銷比進程要小不少。
線程之間的通訊更方便,同一進程下的線程共享全局變量,靜態變量,而進程之間的通訊須要以通訊的方式(IPC)進行。不過如何處理好同步與互斥是編寫多線程程序的難點。
可是多進程程序更健壯,多線程程序只要有一個線程死掉,整個進程也死掉了,而一個進程死掉並不會對另一個進程形成影響,由於進程有本身獨立的地址空間。
也正是因爲GIL鎖的緣由:IO密集型操做可使用多線程,計算密集型可使用多進程。
很差。線程之間進行切換時,要作上下文管理。
不用一直等待的問題。
RLock能夠屢次加鎖。
進程是cpu資源分配的最小單元,一個進程中能夠有多個線程。
線程是cpu計算的最小單元。
對於Python來講他的進程和線程和其餘語言有差別,是有GIL鎖。
GIL鎖保證一個進程中同一時刻只有一個線程被cpu調度。
注意:IO密集型操做可使用多線程,計算密集型可使用多進程。
協程,是由程序員創造出來的一個不是真實存在的東西。
協程,是微線程,對一個線程進程分片,使得線程在代碼塊之間進行來回切換執行,而不是在原來逐行執行。
檢測多個socket是否已經發生變化(是否已經鏈接成功/是否已經獲取數據)(可讀/可寫)IO多路複用做用?
檢測多個socket是否發生變化。
操做系統檢測socket是否發生變化,有三種模式:
Python模塊:
默認是阻塞,填在等待消息和鏈接
非阻塞,不等待。好比建立socket對某個地址進行connect、獲取接收數據recv時默認都會等待(鏈接成功或接收到數據),才執行後續操做。
若是設置setblocking(False),以上兩個過程就再也不等待,可是會報BlockingIOError的錯誤,只要捕獲便可。
異步,通知,執行完成以後自動執行回調函數或自動執行某些操做(通知)。好比作爬蟲中向某個地址baidu。com發送請求,當請求執行完成以後自執行回調函數。
協程也能夠稱爲「微線程」,就是開發者控制線程執行流程,控制先執行某段代碼而後再切換到另外函執行代碼...來回切換。
協程本身自己沒法實現併發(甚至性能會下降)。
協程+IO切換性能提升。
實現併發請求(一個線程100個請求)
import socket # 建立socket client = socket.socket() # 將原來阻塞的位置變成非阻塞(報錯) client.setblocking(False) # 百度建立鏈接: 阻塞 try: # 執行了但報錯了 client.connect(('www.baidu.com',80)) except BlockingIOError as e: pass # 檢測到已經鏈接成功 # 問百度我要什麼? client.sendall(b'GET /s?wd=alex HTTP/1.0\r\nhost:www.baidu.com\r\n\r\n') # 我等着接收百度給個人回覆 chunk_list = [] while True: # 將原來阻塞的位置變成非阻塞(報錯) chunk = client.recv(8096) if not chunk: break chunk_list.append(chunk) body = b''.join(chunk_list) print(body.decode('utf-8'))
from gevent import monkey # 之後代碼中遇到IO都會自動執行greenlet的switch進行切換 monkey.patch_all() import requests import gevent def get_page1(url): ret = requests.get(url) print(url,ret.content) def get_page2(url): ret = requests.get(url) print(url,ret.content) def get_page3(url): ret = requests.get(url) print(url,ret.content) gevent.joinall([ gevent.spawn(get_page1, 'https://www.python.org/'), # 協程1 gevent.spawn(get_page2, 'https://www.yahoo.com/'), # 協程2 gevent.spawn(get_page3, 'https://github.com/'), # 協程3 ])
執行完某我的物後自動調用我給他的函數,非阻塞
import socket import select # 百度建立鏈接:非阻塞 client1 = socket.socket() client1.setblocking(False) try: client1.connect(('www.baidu.com', 80)) except BlockingIOError as e: pass # 搜狗建立鏈接:非阻塞 client2 = socket.socket() client2.setblocking(False) try: client2.connect(('www.sogou.com', 80)) except BlockingIOError as e: pass # GitHub建立鏈接:非阻塞 client3 = socket.socket() client3.setblocking(False) try: client3.connect(('www.github.com', 80)) except BlockingIOError as e: pass # 建立socket列表:socket_list socket_list = [client1, client2, client3] # 建立connect列表:conn_list conn_list = [client1, client2, client3] while True: rlist, wlist, elist = select.select(socket_list, conn_list, [], 0.005) # rlist中表示已近獲取數據的socket對象 # wlist中表示已經鏈接成功的socket對象 # elist中表示出現錯誤的socket對象 for sk in wlist: if sk == client1: sk.sendall(b'GET /s?wd=alex HTTP/1.0\r\nhost:www.baidu.com\r\n\r\n') elif sk == client2: sk.sendall(b'GET /web?query=fdf HTTP/1.0\r\nhost:www.sogou.com\r\n\r\n') else: sk.sendall(b'GET /s?wd=alex HTTP/1.0\r\nhost:www.oldboyedu.com\r\n\r\n') conn_list.remove(sk) for sk in rlist: chunk_list = [] while True: try: chunk = sk.recv(8096) if not chunk: break chunk_list.append(chunk) except BlockingIOError as e: break body = b''.join(chunk_list) # print(body.decode('utf-8')) print('------------>', body) sk.close() socket_list.remove(sk) if not socket_list: break
import socket import select class Req(object): def __init__(self,sk,func): self.sock = sk self.func = func def fileno(self): return self.sock.fileno() class Nb(object): def __init__(self): self.conn_list = [] self.socket_list = [] def add(self,url,func): # 建立socket客戶端 client = socket.socket() # 非阻塞 client.setblocking(False) try: # 建立鏈接 client.connect((url, 80)) # 異常處理 except BlockingIOError as e: pass obj = Req(client,func) # 鏈接列表 self.conn_list.append(obj) # socket列表 self.socket_list.append(obj) def run(self): while True: rlist,wlist,elist = select.select(self.socket_list,self.conn_list,[],0.005) # wlist中表示已經鏈接成功的req對象 for sk in wlist: # 發生變換的req對象 sk.sock.sendall(b'GET /s?wd=alex HTTP/1.0\r\nhost:www.baidu.com\r\n\r\n') self.conn_list.remove(sk) for sk in rlist: chunk_list = [] while True: try: chunk = sk.sock.recv(8096) if not chunk: break chunk_list.append(chunk) except BlockingIOError as e: break body = b''.join(chunk_list) # print(body.decode('utf-8')) sk.func(body) sk.sock.close() self.socket_list.remove(sk) if not self.socket_list: break def baidu_repsonse(body): print('百度下載結果:',body) def sogou_repsonse(body): print('搜狗下載結果:', body) def github_repsonse(body): print('GITHUB下載結果:', body) t1 = Nb() t1.add('www.baidu.com',baidu_repsonse) t1.add('www.sogou.com',sogou_repsonse) t1.add('www.github.com',oldboyedu_repsonse) t1.run()
如Twisted框架,scrapy框架(單線程完成併發)