python中的多工程式設計

xiaotai1234發表於2020-11-07

python中的多工程式設計

一、多工的介紹

1.多工的概念

在這裡插入圖片描述

2.多工的執行方式

在這裡插入圖片描述

3.小結

在這裡插入圖片描述

二、程式

1.程式的介紹

在Python程式中,想要實現多工可以使用程式來完成,程式是實現多工的一種方式。

2.程式的概念

在這裡插入圖片描述

3.程式的作用

在這裡插入圖片描述
在這裡插入圖片描述

4.小結

在這裡插入圖片描述

三、多程式的使用

1.匯入程式包

在這裡插入圖片描述

2.Process程式類的說明

在這裡插入圖片描述

3.多程式完成多工的程式碼

import multiprocessing
import time


# 跳舞任務
def dance():
    for i in range(5):
        print("跳舞中...")
        time.sleep(0.2)


# 唱歌任務
def sing():
    for i in range(5):
        print("唱歌中...")
        time.sleep(0.2)

if __name__ == '__main__':
    # 建立跳舞的子程式
    # group: 表示程式組,目前只能使用None
    # target: 表示執行的目標任務名(函式名、方法名)
    # name: 程式名稱, 預設是Process-1, .....
    dance_process = multiprocessing.Process(target=dance, name="myprocess1")
    sing_process = multiprocessing.Process(target=sing)

    # 啟動子程式執行對應的任務
    dance_process.start()
    sing_process.start()

在這裡插入圖片描述

4.小結

在這裡插入圖片描述

四、獲取程式編號

1.獲取程式編號的目的

在這裡插入圖片描述

2.獲取當前程式編號

在這裡插入圖片描述

import multiprocessing
import time
import os


# 跳舞任務
def dance():
    # 獲取當前程式的編號
    print("dance:", os.getpid())
    # 獲取當前程式
    print("dance:", multiprocessing.current_process())
    for i in range(5):
        print("跳舞中...")
        time.sleep(0.2)
        # 擴充套件:根據程式編號殺死指定程式
        os.kill(os.getpid(), 9)


# 唱歌任務
def sing():
    # 獲取當前程式的編號
    print("sing:", os.getpid())
    # 獲取當前程式
    print("sing:", multiprocessing.current_process())
    for i in range(5):
        print("唱歌中...")
        time.sleep(0.2)


if __name__ == '__main__':

    # 獲取當前程式的編號
    print("main:", os.getpid())
    # 獲取當前程式
    print("main:", multiprocessing.current_process())
    # 建立跳舞的子程式
    # group: 表示程式組,目前只能使用None
    # target: 表示執行的目標任務名(函式名、方法名)
    # name: 程式名稱, 預設是Process-1, .....
    dance_process = multiprocessing.Process(target=dance, name="myprocess1")
    sing_process = multiprocessing.Process(target=sing)

    # 啟動子程式執行對應的任務
    dance_process.start()
    sing_process.start()

在這裡插入圖片描述

3.獲取當前父程式編號

在這裡插入圖片描述

import multiprocessing
import time
import os


# 跳舞任務
def dance():
    # 獲取當前程式的編號
    print("dance:", os.getpid())
    # 獲取當前程式
    print("dance:", multiprocessing.current_process())
    # 獲取父程式的編號
    print("dance的父程式編號:", os.getppid())
    for i in range(5):
        print("跳舞中...")
        time.sleep(0.2)
        # 擴充套件:根據程式編號殺死指定程式
        os.kill(os.getpid(), 9)


# 唱歌任務
def sing():
    # 獲取當前程式的編號
    print("sing:", os.getpid())
    # 獲取當前程式
    print("sing:", multiprocessing.current_process())
    # 獲取父程式的編號
    print("sing的父程式編號:", os.getppid())
    for i in range(5):
        print("唱歌中...")
        time.sleep(0.2)


if __name__ == '__main__':

    # 獲取當前程式的編號
    print("main:", os.getpid())
    # 獲取當前程式
    print("main:", multiprocessing.current_process())
    # 建立跳舞的子程式
    # group: 表示程式組,目前只能使用None
    # target: 表示執行的目標任務名(函式名、方法名)
    # name: 程式名稱, 預設是Process-1, .....
    dance_process = multiprocessing.Process(target=dance, name="myprocess1")
    sing_process = multiprocessing.Process(target=sing)

    # 啟動子程式執行對應的任務
    dance_process.start()
    sing_process.start()

在這裡插入圖片描述

4.小結

在這裡插入圖片描述

五、程式執行帶有引數的任務

1.程式執行帶有引數的任務的介紹

在這裡插入圖片描述

2.args引數的使用

示例程式碼:

import multiprocessing
import time


# 帶有引數的任務
def task(count):
    for i in range(count):
        print("任務執行中..")
        time.sleep(0.2)
    else:
        print("任務執行完成")


if __name__ == '__main__':
    # 建立子程式
    # args: 以元組的方式給任務傳入引數
    sub_process = multiprocessing.Process(target=task, args=(5,))
    sub_process.start()

在這裡插入圖片描述

3.kwargs引數的使用

示例程式碼:

import multiprocessing
import time


# 帶有引數的任務
def task(count):
    for i in range(count):
        print("任務執行中..")
        time.sleep(0.2)
    else:
        print("任務執行完成")


if __name__ == '__main__':
    # 建立子程式

    # kwargs: 表示以字典方式傳入引數
    sub_process = multiprocessing.Process(target=task, kwargs={"count": 3})
    sub_process.start()

在這裡插入圖片描述

4.小結

在這裡插入圖片描述

六、程式的注意點

1.程式的注意點介紹

在這裡插入圖片描述

2.程式之間不共享全域性變數

import multiprocessing
import time

# 定義全域性變數
g_list = list()


# 新增資料的任務
def add_data():
    for i in range(5):
        g_list.append(i)
        print("add:", i)
        time.sleep(0.2)

    # 程式碼執行到此,說明資料新增完成
    print("add_data:", g_list)


def read_data():
    print("read_data", g_list)


if __name__ == '__main__':
    # 建立新增資料的子程式
    add_data_process = multiprocessing.Process(target=add_data)
    # 建立讀取資料的子程式
    read_data_process = multiprocessing.Process(target=read_data)

    # 啟動子程式執行對應的任務
    add_data_process.start()
    # 主程式等待新增資料的子程式執行完成以後程式再繼續往下執行,讀取資料
    add_data_process.join()
    read_data_process.start()

    print("main:", g_list)

    # 總結: 多程式之間不共享全域性變數

在這裡插入圖片描述
在這裡插入圖片描述

3.程式之間不共享全域性變數的小結

在這裡插入圖片描述

4.主程式會等待所有的子程式執行結束再結束

在這裡插入圖片描述

import multiprocessing
import time


# 定義程式所需要執行的任務
def task():
    for i in range(10):
        print("任務執行中...")
        time.sleep(0.2)

if __name__ == '__main__':
    # 建立子程式
    sub_process = multiprocessing.Process(target=task)
    sub_process.start()

    # 主程式延時0.5秒鐘
    time.sleep(0.5)
    print("over")
    exit()

    # 總結: 主程式會等待所有的子程式執行完成以後程式再退出

在這裡插入圖片描述
在這裡插入圖片描述
保證主程式正常退出的示例程式碼:

import multiprocessing
import time


# 定義程式所需要執行的任務
def task():
    for i in range(10):
        print("任務執行中...")
        time.sleep(0.2)

if __name__ == '__main__':
    # 建立子程式
    sub_process = multiprocessing.Process(target=task)
    # 設定守護主程式,主程式退出子程式直接銷燬,子程式的生命週期依賴與主程式
    # sub_process.daemon = True
    sub_process.start()

    time.sleep(0.5)
    print("over")
    # 讓子程式銷燬
    sub_process.terminate()
    exit()

    # 總結: 主程式會等待所有的子程式執行完成以後程式再退出
    # 如果想要主程式退出子程式銷燬,可以設定守護主程式或者在主程式退出之前讓子程式銷燬

在這裡插入圖片描述

5.主程式會等待所有的子程式執行結束再結束的小結

在這裡插入圖片描述

七、執行緒

1.執行緒的介紹

在Python中,想要實現多工除了使用程式,還可以使用執行緒來完成,執行緒是實現多工的另外一種方式。

2.執行緒的概念

在這裡插入圖片描述

3.執行緒的作用

在這裡插入圖片描述

4.小結

在這裡插入圖片描述

八、多執行緒的使用

1.匯入執行緒模組

在這裡插入圖片描述

2.執行緒類Thread引數說明

在這裡插入圖片描述

3.啟動執行緒

啟動執行緒使用start方法

4.多執行緒完成多工的程式碼

import threading
import time

# 唱歌任務
def sing():
    # 擴充套件: 獲取當前執行緒
    # print("sing當前執行的執行緒為:", threading.current_thread())
    for i in range(3):
        print("正在唱歌...%d" % i)
        time.sleep(1)

# 跳舞任務
def dance():
    # 擴充套件: 獲取當前執行緒
    # print("dance當前執行的執行緒為:", threading.current_thread())
    for i in range(3):
        print("正在跳舞...%d" % i)
        time.sleep(1)


if __name__ == '__main__':
    # 擴充套件: 獲取當前執行緒
    # print("當前執行的執行緒為:", threading.current_thread())
    # 建立唱歌的執行緒
    # target: 執行緒執行的函式名
    sing_thread = threading.Thread(target=sing)

    # 建立跳舞的執行緒
    dance_thread = threading.Thread(target=dance)

    # 開啟執行緒
    sing_thread.start()
    dance_thread.start()

在這裡插入圖片描述

5.小結

在這裡插入圖片描述

九、執行緒執行帶有引數的任務

1.執行緒執行帶有引數的任務的介紹

在這裡插入圖片描述

2.args引數的使用

示例程式碼:

import threading
import time


# 帶有引數的任務
def task(count):
    for i in range(count):
        print("任務執行中..")
        time.sleep(0.2)
    else:
        print("任務執行完成")


if __name__ == '__main__':
    # 建立子執行緒
    # args: 以元組的方式給任務傳入引數
    sub_thread = threading.Thread(target=task, args=(5,))
    sub_thread.start()

在這裡插入圖片描述

3.kwargs引數的使用

示例程式碼:

import threading
import time


# 帶有引數的任務
def task(count):
    for i in range(count):
        print("任務執行中..")
        time.sleep(0.2)
    else:
        print("任務執行完成")


if __name__ == '__main__':
    # 建立子執行緒
    # kwargs: 表示以字典方式傳入引數
    sub_thread = threading.Thread(target=task, kwargs={"count": 3})
    sub_thread.start()

在這裡插入圖片描述

4.小結

在這裡插入圖片描述

十、執行緒的注意點

1.執行緒的注意點介紹

在這裡插入圖片描述

2.執行緒之間執行是無序的

import threading
import time


def task():
    time.sleep(1)
    print("當前執行緒:", threading.current_thread().name)


if __name__ == '__main__':

   for _ in range(5):
       sub_thread = threading.Thread(target=task)
       sub_thread.start()

在這裡插入圖片描述

3.主執行緒會等待所有的子執行緒執行結束再結束

假如我們現在建立一個子執行緒,這個子執行緒執行完大概需要2.5秒鐘,現在讓主執行緒執行1秒鐘就退出程式,檢視一下執行結果,示例程式碼如下:

import threading
import time


# 測試主執行緒是否會等待子執行緒執行完成以後程式再退出
def show_info():
    for i in range(5):
        print("test:", i)
        time.sleep(0.5)


if __name__ == '__main__':
    sub_thread = threading.Thread(target=show_info)
    sub_thread.start()

    # 主執行緒延時1秒
    time.sleep(1)
    print("over")

在這裡插入圖片描述
在這裡插入圖片描述
設定守護主執行緒的示例程式碼:

import threading
import time


# 測試主執行緒是否會等待子執行緒執行完成以後程式再退出
def show_info():
    for i in range(5):
        print("test:", i)
        time.sleep(0.5)


if __name__ == '__main__':
    # 建立子執行緒守護主執行緒 
    # daemon=True 守護主執行緒
    # 守護主執行緒方式1
    sub_thread = threading.Thread(target=show_info, daemon=True)
    # 設定成為守護主執行緒,主執行緒退出後子執行緒直接銷燬不再執行子執行緒的程式碼
    # 守護主執行緒方式2
    # sub_thread.setDaemon(True)
    sub_thread.start()

    # 主執行緒延時1秒
    time.sleep(1)
    print("over")

在這裡插入圖片描述

4.執行緒之間共享全域性變數

在這裡插入圖片描述

import threading
import time


# 定義全域性變數
my_list = list()

# 寫入資料任務
def write_data():
    for i in range(5):
        my_list.append(i)
        time.sleep(0.1)
    print("write_data:", my_list)


# 讀取資料任務
def read_data():
    print("read_data:", my_list)


if __name__ == '__main__':
    # 建立寫入資料的執行緒
    write_thread = threading.Thread(target=write_data)
    # 建立讀取資料的執行緒
    read_thread = threading.Thread(target=read_data)

    write_thread.start()
    # 延時
    # time.sleep(1)
    # 主執行緒等待寫入執行緒執行完成以後程式碼在繼續往下執行
    write_thread.join()
    print("開始讀取資料啦")
    read_thread.start()

在這裡插入圖片描述

5.執行緒之間共享全域性變數資料出現錯誤問題

在這裡插入圖片描述

import threading

# 定義全域性變數
g_num = 0


# 迴圈一次給全域性變數加1
def sum_num1():
    for i in range(1000000):
        global g_num
        g_num += 1

    print("sum1:", g_num)


# 迴圈一次給全域性變數加1
def sum_num2():
    for i in range(1000000):
        global g_num
        g_num += 1
    print("sum2:", g_num)


if __name__ == '__main__':
    # 建立兩個執行緒
    first_thread = threading.Thread(target=sum_num1)
    second_thread = threading.Thread(target=sum_num2)

    # 啟動執行緒
    first_thread.start()
    # 啟動執行緒
    second_thread.start()

在這裡插入圖片描述
在這裡插入圖片描述
在這裡插入圖片描述
執行緒等待的示例程式碼:

import threading

# 定義全域性變數
g_num = 0


# 迴圈1000000次每次給全域性變數加1
def sum_num1():
    for i in range(1000000):
        global g_num
        g_num += 1

    print("sum1:", g_num)


# 迴圈1000000次每次給全域性變數加1
def sum_num2():
    for i in range(1000000):
        global g_num
        g_num += 1
    print("sum2:", g_num)


if __name__ == '__main__':
    # 建立兩個執行緒
    first_thread = threading.Thread(target=sum_num1)
    second_thread = threading.Thread(target=sum_num2)

    # 啟動執行緒
    first_thread.start()
    # 主執行緒等待第一個執行緒執行完成以後程式碼再繼續執行,讓其執行第二個執行緒
    # 執行緒同步: 一個任務執行完成以後另外一個任務才能執行,同一個時刻只有一個任務在執行
    first_thread.join()
    # 啟動執行緒
    second_thread.start()

在這裡插入圖片描述

6.小結

在這裡插入圖片描述

十一、互斥鎖

1.互斥鎖的概念

在這裡插入圖片描述

2.互斥鎖的使用

在這裡插入圖片描述

3.使用互斥鎖完成2個執行緒對同一個全域性變數各加100萬次的操作

import threading


# 定義全域性變數
g_num = 0

# 建立全域性互斥鎖
lock = threading.Lock()


# 迴圈一次給全域性變數加1
def sum_num1():
    # 上鎖
    lock.acquire()
    for i in range(1000000):
        global g_num
        g_num += 1

    print("sum1:", g_num)
    # 釋放鎖
    lock.release()


# 迴圈一次給全域性變數加1
def sum_num2():
    # 上鎖
    lock.acquire()
    for i in range(1000000):
        global g_num
        g_num += 1
    print("sum2:", g_num)
    # 釋放鎖
    lock.release()


if __name__ == '__main__':
    # 建立兩個執行緒
    first_thread = threading.Thread(target=sum_num1)
    second_thread = threading.Thread(target=sum_num2)
    # 啟動執行緒
    first_thread.start()
    second_thread.start()

    # 提示:加上互斥鎖,那個執行緒搶到這個鎖我們決定不了,那執行緒搶到鎖那個執行緒先執行,沒有搶到的執行緒需要等待
    # 加上互斥鎖多工瞬間變成單任務,效能會下降,也就是說同一時刻只能有一個執行緒去執行

在這裡插入圖片描述

4.小結

在這裡插入圖片描述

十二、死鎖

1.死鎖的概念

在這裡插入圖片描述

2.死鎖示例

在這裡插入圖片描述

import threading
import time

# 建立互斥鎖
lock = threading.Lock()


# 根據下標去取值, 保證同一時刻只能有一個執行緒去取值
def get_value(index):

    # 上鎖
    lock.acquire()
    print(threading.current_thread())
    my_list = [3,6,8,1]
    # 判斷下標釋放越界
    if index >= len(my_list):
        print("下標越界:", index)
        return
    value = my_list[index]
    print(value)
    time.sleep(0.2)
    # 釋放鎖
    lock.release()


if __name__ == '__main__':
    # 模擬大量執行緒去執行取值操作
    for i in range(30):
        sub_thread = threading.Thread(target=get_value, args=(i,))
        sub_thread.start()

3.避免死鎖

在合適的地方釋放鎖

import threading
import time

# 建立互斥鎖
lock = threading.Lock()


# 根據下標去取值, 保證同一時刻只能有一個執行緒去取值
def get_value(index):

    # 上鎖
    lock.acquire()
    print(threading.current_thread())
    my_list = [3,6,8,1]
    if index >= len(my_list):
        print("下標越界:", index)
        # 當下標越界需要釋放鎖,讓後面的執行緒還可以取值
        lock.release()
        return
    value = my_list[index]
    print(value)
    time.sleep(0.2)
    # 釋放鎖
    lock.release()


if __name__ == '__main__':
    # 模擬大量執行緒去執行取值操作
    for i in range(30):
        sub_thread = threading.Thread(target=get_value, args=(i,))
        sub_thread.start()

4.小結

在這裡插入圖片描述

十三、程式和執行緒的對比

1.程式和執行緒的對比的三個方向

在這裡插入圖片描述

2.關係對比

在這裡插入圖片描述

3.區別對比

在這裡插入圖片描述

4.優缺點對比

在這裡插入圖片描述

5.小結

在這裡插入圖片描述

十四、協程

協程,又稱微執行緒,纖程。英文名Coroutine。

1.協程是啥

在這裡插入圖片描述

2.協程的優點

在這裡插入圖片描述

3.gevent

在這裡插入圖片描述
在這裡插入圖片描述

gevent的使用

在這裡插入圖片描述
在這裡插入圖片描述

gevent切換執行

在這裡插入圖片描述
在這裡插入圖片描述

給程式打補丁

在這裡插入圖片描述
在這裡插入圖片描述
在這裡插入圖片描述
在這裡插入圖片描述

相關文章