1. 程式人生 > >12 認識程序與執行緒 (進階)

12 認識程序與執行緒 (進階)

認識程序與執行緒(python)

  一段時間沒有更新部落格了,今天和大家講講關於 python 程序和執行緒的知識點。(個人心得,多多指教!)

階段一:併發與並行的深入理解

​ 並行一定是併發,但併發不一定是並行。

​ 並行是相對的,並行是絕對的。

1、關於並行與併發的問題引入:

問題一: 計算機是如何執行程式指令的?

問題二: 計算機如何模擬出並行執行的效果?

問題三: 真正的並行需要依賴什麼?

2、計算機執行指令示意圖

2、輪詢排程實現併發執行

併發:看上去一起執行,同時在發生

並行:真正一起執行,同時在進行

排程演算法:

​ 時間片輪轉

​ 優先順序排程

3、並行需要的核心條件

​ 並行真正的核心條件是有多個CPU

階段二:多程序實現並行

1、多程序並行問題引入

問題一: 什麼是程序?

問題二: 如何在Python中使用程序?

問題三: 多程序實現並行的必要條件是什麼?

2、程序的概念

計算機程式是儲存在磁碟上的可執行二進位制(或其他型別)檔案。

​ 只有把它們載入到記憶體中,並被作業系統呼叫它們才會擁有其自己的生命週期。

程序則是表示的一個正在執行的程式。

​ 每個程序都擁有自己的地址空間、記憶體、資料棧以及其他用於跟蹤執行的輔助資料

作業系統負責其上所有程序的執行。

​ 作業系統會為這些程序合理地分配執行時間。

3、在Python中直接執行耗時函式
import time
​
print('main-task start:', time.asctime(time.localtime(time.time())))
​
def func():
    print('sub-task start:', time.asctime(time.localtime(time.time())))
    time.sleep(5)
    print('sub-task end:', time.asctime(time.localtime(time.time())))
​
func()
time.sleep(
5) print('main-task end:', time.asctime(time.localtime(time.time())))
4、在Python中使用程序來分擔耗時任務
import time
import multiprocessing
​
def func(n):
    for i in range(n):
        for a in range(n):
            for b in range(n):
                print(b)
​
start_time = time.time()
​
p = multiprocessing.Process(target=func, args=(50, ))    # 例項化,建立一個程序
# 引數如何傳?  args=(50, )  kwargs={'n': 50}
p.start()   # 開啟程序
p.join()    # 主程序等待子程序結束
​
func(50)
# func(50)
​
end_time = time.time()
print('運行了%ds!' % (end_time - start_time))
5、多程序並行的必要條件

總程序數量不多於CPU核心數量!

​ 因此,現在執行的程式都是輪詢排程產生的並行假象。但是在Python層面的確獲得了並行!

階段三:多執行緒實現併發

1、多執行緒併發問題引入

問題一: 什麼是執行緒?

問題二: 如何在Python中使用執行緒?

問題三: 為什麼多執行緒不是並行?

2、執行緒的概念

執行緒被稱作輕量級程序。

​ 與程序類似,不過它們是在同一個程序下執行的。並且它們會共享相同的上下文。

當其他執行緒執行時,它可以被搶佔(中斷)和臨時掛起(也成為睡眠)— 讓步

​ 執行緒的輪詢排程機制類似於程序的輪詢排程。只不過這個排程不是由作業系統來負責,而是由Python直譯器來負責。

3、在Python中使用執行緒來避開阻塞任務
import time
import multiprocessing
import threading
​
print('---outer--start---:', time.asctime(time.localtime(time.time())))
​
def func():
    print('---inner--start---:', time.asctime(time.localtime(time.time())))
    time.sleep(5)
    print('---inner--end---:', time.asctime(time.localtime(time.time())))
​
"""
在程序裡可以模擬耗時任務,但是線上程裡只能模擬阻塞任務,不能模擬耗時任務。因為多執行緒只有一個核心程序。
"""
p = multiprocessing.Process(target=func)    # 建立子程序
t = threading.Thread(target=func)   # 建立子執行緒
t.start()   # 開啟子執行緒
​
time.sleep(5)
print('---outer--end---:', time.asctime(time.localtime(time.time())))

CPU在任意一個程序裡,任意時刻,只能執行一個執行緒

​ 對程序的輪詢是作業系統負責排程

​ 對執行緒的輪詢是Python直譯器負責排程

4、GIL鎖 全域性直譯器鎖

Python在設計的時候,還沒有多核處理器的概念。

因此,為了設計方便與執行緒安全,直接設計了一個鎖。

這個鎖要求,任何程序中,一次只能有一個執行緒在執行。

因此,並不能為多個執行緒分配多個CPU。

所以Python中的執行緒只能實現併發,

而不能實現真正的並行。

但是Python3中的GIL鎖有一個很棒的設計,

在遇到阻塞(不是耗時)的時候,會自動切換執行緒。

5、GIL鎖帶給我們的新認知

遇到阻塞就自動切換。因此我們可以利用這種機制來有效的避開阻塞~充分利用CPU

階段四:使用多程序與多執行緒來實現併發伺服器

關鍵點一: 多程序是並行執行,

​ 相當於分別獨立得處理各個請求。

關鍵點二: 多執行緒,雖然不能並行執行,

​ 但是可以通過避開阻塞切換執行緒

​ 來實現併發的效果,並且不浪費CUP

from socket import *
from multiprocessing import Process     # 程序
from threading import Thread    # 執行緒
# 建立套接字
server = socket()
server.bind(('', 9999))
server.listen(1000)
​
# 定義函式
def func(conn):
    while True:
        recv_data = conn.recv(1024)
        if recv_data:
            print(recv_data)
            conn.send(recv_data)
        else:
            conn.close()
            break
​
​
while True:   # 迴圈去監聽
    conn, addr = server.accept()
    # 每生成一個對等連線套接字,我就生成一個程序、執行緒,並且我讓這個程序、執行緒去服務這個連線過來的客戶端
# p = Process(target=func, args=(conn, ))    # 生成一個程序
    # p.start()   # 啟動程序
​
​
    t = Thread(target=func, args=(conn, ))    # 生成一個執行緒
    t.start()   # 啟動執行緒