python_並行與併發、多線程
問題一: 計算機是如何執行程序指令的?
問題二: 計算機如何實現併發的?
輪詢調度實現併發執行 程序1-8輪詢完成,纔再CPU上運行
問題三: 真正的並行需要依賴什麼?
並行需要的核心條件
多進程實現並行
問題一: 什麼是進程?
計算機程序是存儲在磁盤上的文件。
只有把它們加載到內存中,並被操作系統調用 它們纔會擁有其自己的生命週期。
進程表示一個正在執行的程序。
每個進程都有獨立地址空間以及其他的輔助數據
進程(Process)
是計算機中已運行程序的實例
問題二: 如何在Python中使用進程?
import multiprocessing # 導入進程模塊
import datetime
import time
def func(data):
while True:
print(datetime.datetime.now())
sum = data + 100
print(sum)
time.sleep(2)
print(datetime.datetime.now())
if __name__ == '__main__':
p = multiprocessing.Process(target=func, args=(123,)) # 創建一個進程,args傳參 必須是元組
p.start() # 運行線程p
while True:
time.sleep(2)
print("我是主進程")
進程使用步驟
問題三: 多進程實現並行的必要條件是什麼?
總進程數量不多於 CPU核心數量!
如果不滿足,那麼運行的程序都是 輪詢調度產生的假象。
多線程實現併發
問題一:什麼是線程?
線程被稱作輕量級進程。
線程是進程中的一個實體,操作系統不會爲進程分配內存空間,它只有一點在運行中必不可少的資源
線程被包含在進程中,是進程中的實際運作單位
同一個進程內的多個線程會共享相同的上下文,
也就是共享資源(內存和數據)。
線程(thread)
是操作系統能夠進行運算調度的最小單位
問題二: 如何在Python中使用線程?
import multiprocessing # 引用進程模塊
import threading # 引用線程模塊
import time
def func(data):
while True:
time.sleep(1)
data += 1
print(data)
# mult = multiprocessing.Process(target=func, args=(1314,))
# mult.start() # 運行進程
thre = threading.Thread(target=func, args=(500,)) # 創建一個線程
thre.start() # 運行線程
print("這是主進程")
進程使用步驟
問題三: 爲什麼多線程不是並行?
穩定性
進程具有獨立的地址空間,一個進程崩潰後,不會對其它進程產生影響。
線程共享地址空間,一個線程非法操作共享數據崩潰後,整個進程就崩潰了。
創建開銷
創建進程操作系統是要分配內存空間和一些其他資源的。開銷很大
創建線程操作系統不需要再單獨分配資源,開銷較小
切換開銷
不同進程直接是獨立的, 切換需要耗費較大資源
線程共享進程地址空間, 切換開銷小
GIL鎖(線程鎖)
Python在設計的時候,還沒有多核處理器的概念。
因此,爲了設計方便與線程安全,直接設計了一個鎖。
這個鎖要求,任何進程中,一次只能有一個線程在執行。
因此,並不能爲多個線程分配多個CPU。
所以Python中的線程只能實現併發,
而不能實現真正的並行。
但是Python3中的GIL鎖有一個很棒的設計,
在遇到阻塞(不是耗時)的時候,會自動切換線程。
很多庫是基於GIL鎖寫的,取消代價太大
進程可以實現並行和併發
線程只能實現併發
遇到阻塞就自動切換。
我們可以利用這種機制來
充分利用CPU
那麼最後:
使用多進程與多線程來實現併發服務器
使用多進程與多線程實現併發服務器的關鍵點
關鍵點一: 多進程是並行執行,
相當於分別獨立得處理各個請求。
關鍵點二: 多線程,雖然不能並行運行,
但是可以通過避開阻塞切換線程
來實現併發的效果,並且不浪費cpu
服務端實現代碼:
import threading # 創建一個線程
import socket
server = socket.socket()
server.bind(('0.0.0.0', 8888))
server.listen() # 監聽
def workon(conn):
while True:
data = conn.recv(1024)
if data == b'':
conn.close()
break
else:
print("接收到的消息: {}".format(data.decode()))
conn.send(data)
# 主線程
while True:
conn, addr = server.accept()
print("{}正在連接".format(addr))
# 線程去處理消息
p = threading.Thread(target=workon, args=(conn,))
p.start()
客戶端代碼:
import socket
click = socket.socket()
click.connect(('127.0.0.1', 8888))
while True:
data = input("請輸入你要發送的數據:")
click.send(data.encode())
print("接收到的消息: {}".format(click.recv(1024).decode()))
總結完成!