執行緒 、進程、協程的基本使用

  • 2021 年 1 月 21 日
  • 筆記

進程(Process)、執行緒(Thread)、協程(Coroutine)的概念:

進程:
就是正在運行的程式,它是作業系統中,資源分配的最小單位.,
資源分配:分配的是cpu和記憶體等物理資源。
進程號是進程的唯一標識。
默認情況下一個進程只有一個執行緒,在進程裡面可以創建多個執行緒。
執行緒:
cpu執行程式的最小單位, 通過執行緒去執行進程中程式碼, 執行緒是執行程式碼的分支,
執行緒是依附在進程裡面的, 沒有進程就沒有執行緒,
同一個程式執行兩次之後是兩個進程
進程和進程之間的關係: 數據彼此隔離,通過socket通訊
協程:
協程是一種用戶態的輕量級執行緒,協程的調度完全由用戶控制。協程擁有自己的暫存器上下文和棧。協程調度切換時,將暫存器上下文和棧保存到其他地方,在切回來的時候,恢復先前保存的暫存器上下文和棧,直接操作棧則基本沒有內核切換的開銷,可以不加鎖的訪問全局變數,所以上下文的切換非常快。
協程在子程式內部可中斷的,然後轉而執行別的子程式,在適當的時候再返回來接著執行。

並行和並發

  • 並發:一個cpu同一時間不停執行多個程式
  • 並行:多個cpu同一時間不停執行多個程式

同步和非同步

  • 同步是指代在程式執行多個任務時,按部就班的依次執行,必須上一個任務執行完有了結果以後,才會執行下一個任務。
  • 非同步是指代在程式執行多個任務時,沒有先後依序,可以同時執行,所以在執行上一個任務時不會等待結果,直接執行下一個任務。一般最終在下一個任務中通過狀態的改變或者通知、回調的方式來獲取上一個任務的執行結果。

cpu的進程調度方法

# 先來先服務fcfs(first come first server):先來的先執行
# 短作業優先演算法:分配的cpu多,先把短的算完
# 時間片輪轉演算法:每一個任務就執行一個時間片的時間.然後就執行其他的.
# 多級回饋隊列演算法

越是時間長的,cpu分配的資源越少,優先順序靠後
越是時間短的,cpu分配的資源越多

進程的性質:

  1. 進程之間的數據彼此隔離
  2. 多個進程之間是非同步並發

–進程(Process):

進程的基本使用:

import os, time, random
from multiprocessing import Process
def func(n):
    time.sleep(random.randrange(3))
    print("數字{}<=>1.子進程id>>{},2父進程id>>{}".format(n, os.getpid(), os.getppid()))
	
if __name__ == "__main__":
    for i in range(1, 11):
        # 創建子進程,返回進程對象,執行func這個任務
        p = Process(target=func, args=(i,))  # args=(i,)傳遞參數
        # 調用子進程
        p.start()  # 可簡寫成 Process(target=func, args=(i,)).start()
    print("主進程執行結束了....")
    print(os.getpid())  # os.getpid()進程號

運行視圖:
在這裡插入圖片描述
運行視圖不唯一,因為多個進程之間是非同步並發

進程的join 函數:

  1. join 等待當前子進程全部執行完畢之後,主進程在執行(用來同步子父進程的)

例:

p = Process(target=func,args=(i,))
p.join()

同步 非同步 / 阻塞 非阻塞

  • 同步:必須等我這件事幹完了,你在干,只有一條主線,就是同步
  • 非同步:沒等我這件事情幹完,你就在幹了,有兩條主線,就是非同步
  • 阻塞:比如程式碼有了input,就是阻塞,必須要輸入一個字元串,否則程式碼不往下執行
  • 非阻塞:沒有任何等待,正常程式碼往下執行.
  • 同步阻塞 :效率低,cpu利用不充分
  • 非同步阻塞 :比如socketserver,可以同時連接多個,但是彼此都有recv
  • 同步非阻塞:沒有類似input的程式碼,從上到下執行.默認的正常情況程式碼

事件 (Event)

  • 非同步非阻塞:效率是最高的,cpu過度充分,過度發熱 液冷

事件 (Event)阻塞事件 :

『』』
e = Event()生成事件對象e
e.wait()動態給程式加阻塞 , 程式當中是否加阻塞完全取決於該對象中的is_set() [默認返回值是False]
# 如果是True 不加阻塞
# 如果是False 加阻塞

# 阻塞事件 :
	e = Event()生成事件對象e   
	e.wait()動態給程式加阻塞 , 程式當中是否加阻塞完全取決於該對象中的is_set() [默認返回值是False]
    # 如果是True  不加阻塞
    # 如果是False 加阻塞

# 控制這個屬性的值
    # set()方法     將這個屬性的值改成True
    # clear()方法   將這個屬性的值改成False
    # is_set()方法  判斷當前的屬性是否為True  (默認上來是False)

Event_test.py:

在這裡插入程式碼片

控制這個屬性的值

# set()方法     將這個屬性的值改成True
# clear()方法   將這個屬性的值改成False
# is_set()方法  判斷當前的屬性是否為True  (默認上來是False)
在這裡插入程式碼片
場景在多任務當中
同步:必須等我這件事幹完了,你在干,只有一條主線,就是同步
非同步:沒等我這件事情幹完,你就在幹了,有兩條主線,就是非同步
阻塞:比如程式碼有了input,就是阻塞,必須要輸入一個字元串,否則程式碼不往下執行
非阻塞:沒有任何等待,正常程式碼往下執行.
 
# 同步阻塞  :效率低,cpu利用不充分
# 非同步阻塞  :比如socketserver,可以同時連接多個,但是彼此都有recv
# 同步非阻塞:沒有類似input的程式碼,從上到下執行.默認的正常情況程式碼
# 非同步非阻塞:效率是最高的,cpu過度充分,過度發熱 液冷

守護進程

守護進程守護的是主進程,如果主進程中的所有程式碼執行完畢了,
當前這個守護進程會被立刻殺死,立刻終止.
語法:
進程.daemon = True 設置當前這個進程為守護進程
必須寫在start()調用進程之前進行設置

默認:主進程會默認等待所有子進程執行結束之後,在關閉程式,釋放資源
程式碼:

from multiprocessing import Process
import time
# (3) 守護進程實際用途: 監控報活
# 守護進行
def alive():
	while True:
		print("給監控的總伺服器發消息,報告自己的存活狀態, i am alive~")
		time.sleep(1)

# 執行任務
def func():
	while True:
		try:
			time.sleep(1)
			raise RuntimeError
			print("當前5號伺服器功能:對日誌進行數據分析.... ")
		except:
			break
			# pass

if __name__ == "__main__":
	# 創建2個子進程
	p1 = Process(target=alive)
	p2 = Process(target=func)
	# 設置p1為守護進程
	# p1.daemon = True
	p1.start()
	p2.start()
	# 必須等到p2任務執行結束之後,在向下執行.
	p2.join()
	print("當前伺服器狀態異常 ... ")


沒有設置守護進程視圖:
在這裡插入圖片描述
設置守護進程視圖:
在這裡插入圖片描述

#可以給子進程貼上守護進程的名字,該進程會隨著主進程程式碼執行完畢而結束(為主進程守護)
(1)守護進程會在主進程程式碼執行結束後就終止
(2)守護進程內無法再開啟子進程,否則拋出異常(了解)

鎖(Lock)

上鎖和解鎖是一對,只上鎖不解鎖會發生死鎖現象(程式碼阻塞,不往下執行了)
互斥鎖 : 互斥鎖是進程之間的互相排斥,誰先搶到這個鎖資源就先使用,後搶到後使用
訊號量:Semaphore 本質上就是鎖,只不過可以控制上鎖的數量
look:
lock.acquire()# 上鎖
lock.release()# 解鎖

# 創建一把鎖
lock = Lock() 
# 上鎖
lock.acquire()
# 連續上鎖不解鎖是死鎖
# lock.acquire() error

print("廁所中...")

# 解鎖
lock.release()
print("執行程式 ... ")

Semaphore:

from multiprocessing import Semaphore,Process
import time,random
sem = Semaphore(4)
sem.acquire()
# sem.acquire()
# sem.acquire()
# sem.acquire()
# sem.acquire() # 上第五把鎖出現死鎖狀態.
print("執行響應的操作") 
sem.release()
"""
# 總結:
Semaphore 可以設置上鎖的數量
同一時間最多允許幾個進程上鎖
創建進程的時候,是非同步並發
執行任務的時候,遇到鎖會變成同步程式.
"""
lock.acquire()# 上鎖
lock.release()# 解鎖
#同一時間允許一個進程上一把鎖 就是Lock
	加鎖可以保證多個進程修改同一塊數據時,同一時間只能有一個任務可以進行修改,即串列的修改,沒錯,速度是慢了,但犧牲速度卻保證了數據安全。
#同一時間允許多個進程上多把鎖 就是[訊號量Semaphore]
	訊號量是鎖的變形: 實際實現是 計數器 + 鎖,同時允許多個進程上鎖	

# 互斥鎖Lock : 互斥鎖就是進程的互相排斥,誰先搶到資源,誰就上鎖改資源內容,為了保證數據的同步性
# 注意:多個鎖一起上,不開鎖,會造成死鎖.上鎖和解鎖是一對.

事件(Event)

# 阻塞事件 :
	e = Event()生成事件對象e   
	e.wait()動態給程式加阻塞 , 程式當中是否加阻塞完全取決於該對象中的is_set() [默認返回值是False]
    # 如果是True  不加阻塞
    # 如果是False 加阻塞

# 控制這個屬性的值
    # set()方法     將這個屬性的值改成True
    # clear()方法   將這個屬性的值改成False
    # is_set()方法  判斷當前的屬性是否為True  (默認上來是False)

進程間通訊 IPC

# IPC Inter-Process Communication
# 實現進程之間通訊的兩種機制:
    # 管道 Pipe
    # 隊列 Queue
    
# put() 存放
# get() 獲取
# get_nowait() 拿不到報異常
# put_nowait() 非阻塞版本的put
q.empty()      檢測是否為空  (了解)
q.full() 	   檢測是否已經存滿 (了解)

–執行緒(Thread)

生產者與消費者模型

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-LYUQwS07-1611230808561)(assets/生產者消費者.png)]