課堂引入

python中的多線程其實並不是真正的多線程,如果想要充分地使用多核CPU的資源,在python中大部分情況需要使用多進程。Python提供了非常好用的多進程包multiprocessing,只需要定義一個函數,Python會完成其他所有事情。藉助這個包,可以輕鬆完成從單進程到併發執行的轉換。multiprocessing支持子進程、通信和共享數據、執行不同形式的同步,提供了Process、Queue、Pipe、Lock等組件。不過,Python雖然不能利用多線程實現多核任務,但可以通過多進程實現多核任務。多個Python進程有各自獨立的GIL鎖,互不影響。

1、進程簡介

日常生活中我們使用能夠的每個程序比如qq、微信等這些程序沒有啓動之前就是一些代碼的集合,是靜態的,當他們運行起來之後,這些靜態的代碼加上用到的資源比如內存、攝像頭等這個整體稱為進程,它是系統分配資源的基本單元,進程是程序的基本執行實體。

2、進程的定義

狹義定義:進程是正在運行的程序的實例(an instance of a computer program that is being executed)。

廣義定義:進程是一個具有一定獨立功能的程序關於某個數據集合的一次運行活動。它是操作系統動態執行的基本單元,在傳統的操作系統中,進程既是基本的分配單元,也是基本的執行單元,每一個運行着的程序至少有一個進程。

為了更好的理解進程,舉個例子:

我們啓動QQ就是啓動了一個進程, 這個進程包括了qq本身的代碼以及qq所使用的資源(內存、顯卡、攝像頭、麥克風等),如果我們同時運行兩個qq那麼此時就會有兩個進程。

3、進程的狀態

程序執行的一個理想狀態是一個cpu核心開啓一個進程執行一個程序,但是工作中非常不現實,及其浪費資源,我們的任務數量遠遠大於cpu核心數,也就數説一個核心會同時接受多個任務,而一個cpu核心同時只能執行一個任務,因此一定有一些任務正在執行,而另一些任務在等待cpu執行, 因此進程有同的狀態。

就緒狀態: 運行的條件都已近準備好,正在等待cpu執行

執行狀態: cpu正在執行中

等待狀態: 等待某些條件滿足,比如某個地方阻塞了1秒,此時會進入等待狀態

1_python高階_數據

4、進程的創建-multiprocessing

(1) Multiprocessing

先要導入multiprocessing模塊: import multiprocessing

函數式:調用 multiprocessing 模塊中的Process()函數來產生新進程。

語法如下:Process([group [, target [, name [, args [, kwargs]]]]])

參數説明:

  • target:如果傳遞了函數的引用,可以任務這個子進程就執行這裏的代碼
  • args:給target指定的函數傳遞的參數,以元組的方式傳遞
  • kwargs:給target指定的函數傳遞命名參數
  • name:給進程設定一個名字,可以不設定
  • group:指定進程組,大多數情況下用不到

下面是創建多進程的一個實例。

import time
import multiprocessing
def test1():
    while True:
        print("函數1執行")
        time.sleep(1)
def test2():
    while True:
        print("函數2執行")
        time.sleep(1)
if __name__ == '__main__':
    p1 = multiprocessing.Process(target=test2)  # 創建線程p1
    p2 = multiprocessing.Process(target=test1)  # 創建線程p2
    p1.start()  #啓動線程
    p2.start()
    print(p1.name) # 輸出進程名字
    print(p2.name)
    print("主進程執行完畢")

運行結果如下:

F:\python_lj\python.exe F:/woniu_python/hm_python/多任務/進程/進程.py
Process-1
Process-2
主進程執行完畢
函數2執行
函數1執行
函數2執行
函數1執行
函數2執行
函數1執行
函數2執行

説明:

上面例子中先導入了multiprocession模塊,然後創建了test1和test2兩個函數, 在main函數中創建了兩個進程,進程的參數target指向需要執行的代碼(注意指向函數是不需要再後面加括號,加括號表示調用函數,不加括號表示執行函數,他們有本質的區別),start()方法啓動進程。兩個函數可以同時執行就像一個人同時做兩件事情,吃飯和睡覺。

5、進程之間通信-消息隊列(Queue)

由於進程之間不能共享全局變量,那麼進程之間通信就得使用其他的方式, 操作系統提供了很多機制來實現進程之間的通信使用multiprocessing模塊中的Queue實現多進程之間的數據傳遞。

from multiprocessing import Queue
q=Queue(3) #初始化一個Queue對象,最多可接收三條3消息,如果不傳參表示隊列長度不定
q.put("第一個消息")
q.put("第二個消息")
print(q.full())  #判斷隊列是否已滿
# 注意: 如果消息隊列已經滿了會在這裏一直等着,可以使用put_nowait():不會等直接報錯,只要捕獲異常不處理就好
q.put("第三個消息")
print(q.empty()) #判斷隊列是否為空
if not q.full():
    q.put("第四個消息")
#讀取消息時,先判斷消息列隊是否為空,再讀取
#q.get() : 如果隊列為空會一直等着,直到有數據為止
if not q.empty():
    for i in range(q.qsize()):
        print(q.get())

運行結果

False
False
第一個消息
第二個消息
第三個消息

説明:

初始化Queue()對象時(例如:q=Queue()),若括號中沒有指定最大可接收的消息數量,或數量為負值,那麼就代表可接受的消息數量沒有上限(直到內存的盡頭);

Queue.qsize():返回當前隊列包含的消息數量;

Queue.empty():如果隊列為空,返回True,反之False ;

Queue.full():如果隊列滿了,返回True,反之False;

Queue.get([block[, timeout]]):獲取隊列中的一條消息,然後將其從列隊中移除,block默認值為True。

  • 如果block使用默認值,且沒有設置timeout(單位秒),消息列隊如果為空,此時程序將被阻塞(停在讀取狀態),直到從消息列隊讀到消息為止,如果設置了timeout,則會等待timeout秒,若還沒讀取到任何消息,則拋出”Queue.Empty”異常。
  • 如果block值為False,消息列隊如果為空,則會立刻拋出”Queue.Empty”異常;
  • Queue.get_nowait():相當Queue.get(False)。
  • Queue.put(item,[block[, timeout]]):將item消息寫入隊列,block默認值為True;
  • 如果block使用默認值,且沒有設置timeout(單位秒),消息列隊如果已經沒有空間可寫入,此時程序將被阻塞(停在寫入狀態),直到從消息列隊騰出空間為止,如果設置了timeout,則會等待timeout秒,若還沒空間,則拋出”Queue.Full”異常;
  • 如果block值為False,消息列隊如果沒有空間可寫入,則會立刻拋出”Queue.Full”異常;
  • Queue.put_nowait(item):相當Queue.put(item, False)。

6、使用隊列完成多進程之間數據共享

案例4:創建一個隊列,用進程1向隊列中添加值,然後用進程2去讀取隊列中的值,實現進程的信息的交互,代碼如下:

from multiprocessing import Process, Queue
import os, time
# 向隊列中添加數據:
def write(q):
    li = ['aa', 'bb', 'cc']
    for value in li:
        print('存入的數據:' ,value)
        q.put(value) # 向隊列中添加數據
        time.sleep(0.5)
# 讀數據進程執行的代碼:
def read(q):
    while True:
        if not q.empty():
            value = q.get()
            print('讀取到的數據:',value)
            time.sleep(0.5)
        else:
            break
if __name__=='__main__':
    q = Queue() # 創建一個隊列
    pw = Process(target=write, args=(q,)) # 創建一個進程將隊列的引用傳過去
    pr = Process(target=read, args=(q,))
    pw.start()
    pw.join()
    pr.start()
    pr.join()
    print('數據讀寫完畢')

運行結果:

存入的數據: aa
存入的數據: bb
存入的數據: cc
讀取到的數據: aa
讀取到的數據: bb
讀取到的數據: cc
數據讀寫完畢

課程小結

1、每個進程是系統內獨立分配資源的基本單位。

2、進程間可以通過隊列實現數據共享。