python 基礎(二)阻塞 非阻塞 同步 異步 應用於multiprocess.pool 進程池 以及EDA FPGA

python基礎系列 正在持續更新中:)

阻塞與非阻塞執行 同步與異步調用 順序與並序

兩個pool進程池的兩種調用函數:

名稱 英文 中文 進程執行方式 備註
pool.apply() synchronous 同步調用 阻塞執行(blocking) 效率低
pool.apply_aync() asynchronous 異步調用 進程是非阻塞執行(non-blocking) 效率高

概念容易混雜,我們通過我自編自導的一個實例來說明

阻塞與非阻塞執行
同步與異步調用
順序與並序

異步調用 非阻塞執行 並行

理髮店(進程池 pool)有兩個理髮師1,2(被調用的進程),來了兩個顧客Mr.A,Mr.B,給客人理髮就是理髮師的任務 (函數需要計算的事項 任務),
理髮店老闆 (也就是調用子進程的父進程 調用理髮師的老闆),居高臨下的審視着一切。

老闆,都喜歡高效做事,打算用 異步調用 asynchronous 的方法 apply_aync()
然後進程(理髮師)是非阻塞執行的
也就是:
老闆 先調用一個理髮師(比如CPU選擇理髮師1)先去給一位顧客(比如Mr.A)理髮,不管Mr.A是否理完髮(進程是否執行完成),馬上繼續安排另一個理髮師給另一個顧客理髮,依次把所有理髮師都安排上,直到,理髮師人手用完了(進程池的進程數滿了),只能讓客戶等着(任務暫時掛起 等待
當然了,作爲員工,肯定需要反饋老闆的安排,回一句“Yes Sir”,所以要意思意思,那是怎麼個意思,那就是這個意思(函數返回一下,但是,任務並沒有完成,應該說剛剛開始做任務)。

對被調用的進程而言(理髮師 打工的),他們工作是非阻塞的non-blocking ,我第二個理髮師接單,不會被第一個理髮師阻礙,阻塞

對於操着上帝視角的我們, 看着可憐的理髮師1,2(被調用的兩個進程),就知道他們是**並行工作(parallel)**的。

Q1:我們代碼寫着理髮師1和Mr.A 在最前面,是不是第一個處理的一定是1呢?
A1:不是,CPU先處理調度哪個是不確定的 所以是1先還是2先,都不可控制,也無所謂(異步嘛,不影響別人)

對老闆而言,一方面他要調用員工,一方面要獲取員工的工作狀態,調用員工以後不等員工工作的結果直接安排其他員工做事,就是異步調用。那麼如果安排一個員工,並一直眼巴巴等待員工執行完成的結果,就是同步調用

同步調用 阻塞執行 串行

異步調用說了,那麼,同步調用會怎麼樣呢?
同樣,理髮店還是有兩個理髮師1,2(被調用的進程),來了兩個顧客Mr.A Mr.B,
理髮店老闆 ,打算用 同步調用 synchronous 的方法 apply()
因爲理髮店只有一個理髮的位置QAQ

老闆 先調用一個理髮師(比如CPU選擇理髮師1)先去給一位顧客(比如Mr.A)理髮,然後焦急的等待理髮師1 爲 Mr.A 理完髮(進程是否執行完成),才能繼續安排另一個理髮師給另一個顧客理髮,
終於 理髮師1理完了,理髮師給老闆說一聲(函數返回),另一位理髮師(比如理髮師2)才能上去。注意沒有函數回調了,只有返回。

對被調用的進程而言(理髮師 打工的),他們工作是阻塞的, 我第二個人接單會被第一個理髮師阻塞。

對於操着上帝視角的我們, 看着可憐的理髮師1,2(被調用的兩個進程),就知道他們是**串行工作(sequencial or serial)**的。

對老闆而言,一方面他要調用員工,一方面要獲取員工的工作狀態,如果安排一個員工,並一直眼巴巴等待員工執行完成的結果,就是同步調用

同步調用,這種事情可能發生在

FGA 抽卡
三崩子 抽老婆

按下那神聖的按鍵(調用),這時我們只會雙眼焦急的盯屏幕(幹不了別的事 而是等待),只見一道聖光緩緩輸出,我們還是不做任何事,直到老婆出現:)
沒事,我老婆不是抽卡出來的2333,saber 不支持任何辯駁 準備拔劍吧,少年
在這裏插入圖片描述

FPGA 阻塞賦值 非阻塞賦值

所以凡是幹活的(進程 命令 語句),後一個會因爲前一個事沒幹完,導致自己也幹不了(第一個阻塞第二個的)就稱爲阻塞,反之爲非阻塞,比如FPGA的阻塞賦值與非阻塞賦值。我們可以結合這個根源來加深理解。

我們設定a=55 b=c=0(初始值)

阻塞賦值:前面語句(可以看作是被調用的語句 幹活的)執行完,纔可執行下一條語句;即:前面語句(理髮師1)的執行(b=a)阻塞了後面語句(理髮師2)的執行(c=b)。

上帝視角看來 2條語句順序執行
而我們是同步調用兩者的
兩個語句間 阻塞

always @(posedge clock)
begin
         b = a;
         c = b;
end

注意FPGA always@(posedge clock)是吧clock的上升沿設爲敏感信號,每當上升沿到來,就執行always語句裏面的代碼。在同一個時鐘上升沿完成,如下圖仿真結果,從上到下分別是clock,rst,a,b,c 五個信號,請無視rst信號(第二個)。
在這裏插入圖片描述
非阻塞賦值:前面語句(理髮師1)的執行(b=a)不會阻塞後面(理髮師2)語句的執行(c=b)

always @(posedge i_clk)
begin
         b <= a;
         c <= b;
end

上帝視角看來 2條語句並序執行
而我們是異步調用兩者的
兩個語句間 非阻塞

第1個clock上升沿,
》a的值賦給b
》b的值賦給c
第二句,開始時,b仍然是初始值0,b還沒有更新,等到兩句並行執行結束
》b從a得到了55
》c從b那邊得到0

第2個clock上升沿,
》a的值賦給b
》b的值賦給c
並行執行完
》b從a得到了55
》c從b那邊得到55

所以c獲得a的值,需要2個clk完成。
如下圖仿真結果:從上到下分別是clock,rst,a,b,c 五個信號,請無視rst信號(第二個)。

在這裏插入圖片描述
其實非阻塞賦值,綜合synthesis出的是兩個鎖存器latch,而阻塞賦值是直接連線的(類似wire)。

python應用

非阻塞式執行 異步調用

#-*- utf-8 -*-
from time import sleep,time
from random import random
import os
from multiprocessing import Process
from multiprocessing import Pool


def ftask(task_name):
    print("開始理髮",task_name)
    start = time()
    a = random()
    sleep(a if(a>0.4) else a+0.4)
    end = time()
    return " {}發理完了,用時:{},進程id:{}".format(task_name,(end-start), os.getpid() )

list_barber = []

def fcallback(n):
    list_barber.append(n)

if __name__ == '__main__':
    pool = Pool(5)

    task_set = ["Mr.A","Mr.B","Mr.C","Mr.D","Mr.E","Mr.F","Mr.G"]

    for task in task_set:
        pool.apply_async(ftask,args=(task,),callback=fcallback)
    pool.close()
    pool.join()

    for barber in list_barber:
        print(barber)

結果如下:

開始理髮 Mr.A
開始理髮 Mr.B
開始理髮 Mr.C
開始理髮 Mr.D
開始理髮 Mr.E
開始理髮 Mr.F
開始理髮 Mr.G
 Mr.A發理完了,用時:0.7314853668212891,進程id:12224
 Mr.B發理完了,用時:0.7336766719818115,進程id:18748
 Mr.D發理完了,用時:0.8218517303466797,進程id:18736
 Mr.E發理完了,用時:0.8295514583587646,進程id:9980
 Mr.C發理完了,用時:0.998112678527832,進程id:20308
 Mr.F發理完了,用時:0.4738035202026367,進程id:12224
 Mr.G發理完了,用時:0.9274580478668213,進程id:18748

阻塞式調用 同步調用

#-*- utf-8 -*-
from time import sleep,time
from random import random
import os
from multiprocessing import Process
from multiprocessing import Pool


def ftask(task_name):
    print("開始理髮",task_name)
    start = time()
    a = random()
    sleep(a if(a>0.4) else a+0.4)
    end = time()
    print(" {}發理完了,用時:{},進程id:{}".format(task_name,(end-start), os.getpid() ))

list_barber = []

def fcallback(n):
    list_barber.append(n)

if __name__ == '__main__':
    pool = Pool(5)

    task_set = ["Mr.A","Mr.B","Mr.C","Mr.D","Mr.E","Mr.F","Mr.G","Mr.H"]

    for task in task_set:
        pool.apply(ftask,args=(task,))

    pool.close()
    pool.join()

    print("main process ended")

結果如下:

開始理髮 Mr.A
 Mr.A發理完了,用時:0.5076336860656738,進程id:17828
開始理髮 Mr.B
 Mr.B發理完了,用時:0.9321200847625732,進程id:19788
開始理髮 Mr.C
 Mr.C發理完了,用時:0.47928452491760254,進程id:19548
開始理髮 Mr.D
 Mr.D發理完了,用時:0.439760684967041,進程id:19112
開始理髮 Mr.E
 Mr.E發理完了,用時:0.9770023822784424,進程id:11848
開始理髮 Mr.F
 Mr.F發理完了,用時:0.45802879333496094,進程id:17828
開始理髮 Mr.G
 Mr.G發理完了,用時:0.5110907554626465,進程id:19788
開始理髮 Mr.H
 Mr.H發理完了,用時:0.7647738456726074,進程id:19548
main process ended

總結 六級單詞

敲黑板 單詞表:

中文 中文
synchronous 同步同時 asynchronous 異步 不同時
serial sequential 串行 順序 parallel concurrent 並行併發
blocking 阻塞 non-blocking 非阻塞

下一節我們就迎來了進程的組成部分——線程,特點,顯而易見更加硬核 更加深入的理解進程與線程:python 基礎(三) 線程 Threading GIL 線程同步

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章