日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

一文搞懂Python中的進程,線程和協程_python

作者:Mr.Winter` ? 更新時間: 2022-06-28 編程語言

1.什么是并發編程

并發編程是實現多任務協同處理,改善系統性能的方式。Python中實現并發編程主要依靠

進程(Process):進程是計算機中的程序關于某數據集合的一次運行實例,是操作系統進行資源分配的最小單位

線程(Thread):線程被包含在進程之中,是操作系統進行程序調度執行的最小單位

協程(Coroutine):協程是用戶態執行的輕量級編程模型,由單一線程內部發出控制信號進行調度

直接上一張圖看看三者概念間的關系。

這張圖說明了什么?首先,一條線程是進程中一個單一的順序控制流,一個進程可以并發多個線程執行不同任務。協程由單一線程內部發出控制信號進行調度,而非受到操作系統管理,因此協程沒有切換開銷和同步鎖機制,具有極高的執行效率。

進程、線程、協程間的特性決定了它們的應用場景不同:

協程常用于IO密集型工作,例如網絡資源請求等;而進程、線程常用于計算密集型工作,例如科學計算、人工神經網絡等。

接下來對每種并發編程方法進行詳細闡述。

2.進程與多進程

Python多進程依賴于標準庫mutiprocessing,進程類Process的常用方法如下

序號 方法 含義
1 start() 創建一個Process子進程實例并執行該實例的run()方法
2 run() 子進程需要執行的目標任務
3 join() 主進程阻塞等待子進程直到子進程結束才繼續執行,可以設置等待超時時間timeout
4 terminate() 終止子進程
5 is_alive() 判斷子進程是否終止
6 daemon 設置子進程是否隨主進程退出而退出

創建多進程任務的實例如下

import os, time
import multiprocessing

class myProcess(multiprocessing.Process):
    def __init__(self, *args, **kwargs) -> None:
        super().__init__()
        self.name = kwargs['name']

    def run(self):
        print("process name:", self.name)
        for i in range(10):
            print(multiprocessing.current_process(), "process pid:",
                  os.getpid(), "正在執行...")
            time.sleep(0.2)

if __name__ == "__main__":
    task = myProcess(name="testProcess")
    task.start()
    task.join()		# 該語句會阻塞主進程直至子進程結束
    print("----------------")

注意:Windows系統在子進程結束后會立即自動清除子進程實例;而Linux系統子進程實例僅當主進程結束后才被回收,在子進程結束但主進程仍在運行的時間內處于僵尸進程狀態,會造成性能損失甚至死鎖。對子進程實例的手動回收可以通過

p.terminate()
p.join()

完成,此外,start()函數也有清除僵尸進程的功能。在使用多進程處理任務時并非進程越多越好,因為進程切換會造成性能開銷。

3.線程與多線程

Python多線程依賴于標準庫threading,線程類Thread的常用方法如下表:

序號 方法 含義
1 start() 創建一個Thread子線程實例并執行該實例的run()方法
2 run() 子線程需要執行的目標任務
3 join() 主進程阻塞等待子線程直到子線程結束才繼續執行,可以設置等待超時時間timeout
4 is_alive() 判斷子線程是否終止
5 daemon 設置子線程是否隨主進程退出而退出

關于線程與進程的關系,還有一個很生動的例子

把一條公路看作一道進程,那么公路上的各個車道就是進程中的各個線程。這些線程(車道)共享了進程(道路)的公共資源;這些線程(車道)之間可以并發執行(各個車道相對獨立),也可以互相同步(交通信號燈)。

rsrc = 10
lock = threading.Lock()

def task1(name):
    global rsrc, lock
    for i in range(5):
        with lock:
            rsrc += 1
            print("task1:", rsrc)
    return name + "has been done!"

def task2(name):
    global rsrc, lock
    for i in range(5):
        lock.acquire()
        rsrc -= 1
        print("task2:", rsrc)
        lock.release() 
    return name + "has been done!"

結果如下

在多線程并發過程中,若沒有控制好線程間的執行邏輯,將可能產生死鎖現象,可以使用with關鍵詞在線程訪問臨界區結束后自動釋放鎖,也可使用release()方法手動釋放句柄。

4.協程與多協程

協程適用于I/O密集型而非計算密集型場景。在協程發起I/O請求后返回結果前往往有大量閑置時間——該時間可能用于網絡數據傳輸、獲取協議頭、服務器查詢數據庫等,而I/O請求本身并不耗時,因此協程可以發送一個請求后讓渡給系統干別的事,這就是協程提高性能的原因。

協程編程的框架如下:

  • 創建協程對象并將其封裝成任務實例;
  • 創建事件循環實例并監聽任務隊列;
  • 獲取協程結果(可在事件循環結束后獲取,或提前添加回調函數)。

一個嵌套協程的示例如下:

import asyncio, time

# 內層協程
async def do_some_work(x):
    print('Waiting: ', x)
    await asyncio.sleep(x)
return 'Done after {}s'.format(x)

def OnCallBack(res):
print(res.result())

# 外層協程main
async def main():
    # 創建三個協程對象并封裝成任務
    task1 = asyncio.ensure_future(do_some_work(1))
    task2 = asyncio.ensure_future(do_some_work(8))
    task3 = asyncio.ensure_future(do_some_work(4))
    # 添加回調
    task1.add_done_callback(OnCallBack)
    task2.add_done_callback(OnCallBack)
    task3.add_done_callback(OnCallBack)
    # 內層任務列表
    tasks = [task1, task2, task3]
	# 將列表轉為可等待對象
    dones, pendings = await asyncio.wait(tasks)

# 外層協程func
async def func():
    for i in range(5):
        print("func:", i)

# 外層任務列表
tasks = [asyncio.ensure_future(func()), asyncio.ensure_future(main())]
# 創建事件循環
loop = asyncio.get_event_loop()
start = time.time()
# 監聽異步任務
loop.run_until_complete(asyncio.wait(tasks))
end = time.time()
print("總耗時:", end - start)

5.總結

看了這么多概念可能有點暈了,下面這張表總結了本文的內容。總得來說,進程、線程、協程各有各的應用場景,不能說多進程、多線程、多協程就一定好,而是要根據具體的使用情況來確定。

原文鏈接:https://blog.csdn.net/FRIGIDWINTER/article/details/124369567

欄目分類
最近更新