進程
程序運行中會有一個默認的進程,如果想要創(chuàng)建子進程,需要用miltiprocessing實現(xiàn)倡蝙。
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__': # 主進程在執(zhí)行主程序
# 創(chuàng)建跳舞的子進程
# group: 表示進程組,目前只能使用None
# target: 表示執(zhí)行的目標任務名(函數(shù)名绞佩、方法名)
# name: 進程名稱, 默認是Process-1, .....
dance_process = multiprocessing.Process(target=dance, name="myprocess1") #這里創(chuàng)建子進程
sing_process = multiprocessing.Process(target=sing) # 這里創(chuàng)建另外一個子進程
# 啟動子進程執(zhí)行對應的任務
dance_process.start()
sing_process.start()
具體哪個進程先執(zhí)行寺鸥,是由操作系統(tǒng)取調(diào)度的猪钮,程序是不能決定的。
獲取進程編號
os.getpid()
獲取當前進程編號胆建,os.getppid()
獲取父進程編號躬贡。multiprocessing.current_process()
獲取當前進程對象。
import multiprocessing
import time
import os
# 跳舞任務
def dance():
# 獲取當前進程的編號
print("dance:", os.getpid())
# 獲取當前進程
print("dance:", multiprocessing.current_process())
# 獲取父進程編號
print('parent:',os.getppid())
for i in range(5):
print("跳舞中...")
time.sleep(0.2)
# 擴展:根據(jù)進程編號殺死指定進程
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())
# 創(chuàng)建跳舞的子進程
# group: 表示進程組眼坏,目前只能使用None
# target: 表示執(zhí)行的目標任務名(函數(shù)名、方法名)
# name: 進程名稱, 默認是Process-1, .....
dance_process = multiprocessing.Process(target=dance, name="myprocess1")
sing_process = multiprocessing.Process(target=sing)
# 啟動子進程執(zhí)行對應的任務
dance_process.start()
sing_process.start()
進程執(zhí)行帶有參數(shù)的任務
Process類執(zhí)行任務并給任務傳參數(shù)有兩種方式:
- args 表示以元組的方式給執(zhí)行任務傳參(位置參數(shù))
- kwargs 表示以字典方式給執(zhí)行任務傳參(關鍵字參數(shù))
- args參數(shù)的使用
import multiprocessing
import time
# 帶有參數(shù)的任務
def task(count):
for i in range(count):
print("任務執(zhí)行中..")
time.sleep(0.2)
else:
print("任務執(zhí)行完成")
if __name__ == '__main__':
# 創(chuàng)建子進程
# args: 以元組的方式給任務傳入?yún)?shù)
sub_process = multiprocessing.Process(target=task, args=(5,))
sub_process.start()
- kwargs參數(shù)的使用
import multiprocessing
import time
# 帶有參數(shù)的任務
def task(count):
for i in range(count):
print("任務執(zhí)行中..")
time.sleep(0.2)
else:
print("任務執(zhí)行完成")
if __name__ == '__main__':
# 創(chuàng)建子進程
# kwargs: 表示以字典方式傳入?yún)?shù)
sub_process = multiprocessing.Process(target=task, kwargs={"count": 3})
sub_process.start()
進程注意點
進程之間不共享全局變量
import multiprocessing
import time
# 定義全局變量
g_list = list()
# 添加數(shù)據(jù)的任務
def add_data():
for i in range(5):
g_list.append(i)
print("add:", i)
time.sleep(0.2)
# 代碼執(zhí)行到此酸些,說明數(shù)據(jù)添加完成
print("add_data:", g_list)
def read_data():
print("read_data", g_list)
if __name__ == '__main__':
# 創(chuàng)建添加數(shù)據(jù)的子進程
add_data_process = multiprocessing.Process(target=add_data)
# 創(chuàng)建讀取數(shù)據(jù)的子進程
read_data_process = multiprocessing.Process(target=read_data)
# 啟動子進程執(zhí)行對應的任務
add_data_process.start()
# 主進程等待添加數(shù)據(jù)的子進程執(zhí)行完成以后程序再繼續(xù)往下執(zhí)行宰译,讀取數(shù)據(jù)
add_data_process.join()
read_data_process.start()
print("main:", g_list)
# 總結(jié): 多進程之間不共享全局變量
add_data_process.join()
這句話表示主進程得等add_data_process進程執(zhí)行完才能往下執(zhí)行。
為什么不共享變量魄懂?
因為子進程其實就是主進程的一個副本沿侈,是對主進程資源進行拷貝。
注意:在windows中一定要寫if __name__ == '__main__':
市栗;這樣子進程中就不會拷貝name=='__main'代碼下面的代碼缀拭。
主進程會等待子進程都執(zhí)行結(jié)束后再結(jié)束
如果想要實現(xiàn)主進程結(jié)束,子進程也結(jié)束填帽,有兩種方法:
- 讓子進程設置稱為守護主進程蛛淋,主進程退出,子進程立刻銷毀篡腌。此時褐荷,子進程依賴于主進程
if __name__ == '__main__':
# 創(chuàng)建子進程
sub_process = multiprocessing.Process(target=task)
# 設置守護主進程,主進程退出子進程直接銷毀嘹悼,子進程的生命周期依賴與主進程
sub_process.daemon = True
sub_process.start()
- 退出主進程之前叛甫,將子進程銷毀
import multiprocessing
import time
# 定義進程所需要執(zhí)行的任務
def task():
for i in range(10):
print("任務執(zhí)行中...")
time.sleep(0.2)
if __name__ == '__main__':
# 創(chuàng)建子進程
sub_process = multiprocessing.Process(target=task)
# 設置守護主進程,主進程退出子進程直接銷毀杨伙,子進程的生命周期依賴與主進程
# sub_process.daemon = True
sub_process.start()
time.sleep(0.5)
print("over")
# 讓子進程銷毀
sub_process.terminate()
exit()
# 總結(jié): 主進程會等待所有的子進程執(zhí)行完成以后程序再退出
# 如果想要主進程退出子進程銷毀其监,可以設置守護主進程或者在主進程退出之前讓子進程銷毀
線程(負責執(zhí)行進程中的代碼)
也是實現(xiàn)多任務的方式
線程是進程中執(zhí)行代碼的一個分支
線程是CPU調(diào)度的基本單位
每個進程至少都有一個線程,稱為主線程
同一個進程中的線程可以實現(xiàn)資源共享
多線程的使用
import threading
import time
# 唱歌任務
def sing():
# 擴展: 獲取當前線程
# print("sing當前執(zhí)行的線程為:", threading.current_thread())
for i in range(3):
print("正在唱歌...%d" % i)
time.sleep(1)
# 跳舞任務
def dance():
# 擴展: 獲取當前線程
# print("dance當前執(zhí)行的線程為:", threading.current_thread())
for i in range(3):
print("正在跳舞...%d" % i)
time.sleep(1)
if __name__ == '__main__':
# 擴展: 獲取當前線程
# print("當前執(zhí)行的線程為:", threading.current_thread())
# 創(chuàng)建唱歌的線程
# target: 線程執(zhí)行的函數(shù)名
sing_thread = threading.Thread(target=sing)
# 創(chuàng)建跳舞的線程
dance_thread = threading.Thread(target=dance)
# 開啟線程
sing_thread.start()
dance_thread.start()
線程執(zhí)行帶有參數(shù)的任務
Thread類執(zhí)行任務并給任務傳參數(shù)有兩種方式:
- args 表示以元組的方式給執(zhí)行任務傳參
- kwargs 表示以字典方式給執(zhí)行任務傳參
線程注意點
- 線程執(zhí)行是無序的
- 主線程會等待所有子線程執(zhí)行完成再結(jié)束
# 守護主線程方式1
sub_thread = threading.Thread(target=show_info, daemon=True)
# 設置成為守護主線程限匣,主線程退出后子線程直接銷毀不再執(zhí)行子線程的代碼
# 守護主線程方式2
# sub_thread.setDaemon(True)
- 線程之間共享全局變量
- 線程之間共享全局變量數(shù)據(jù)出現(xiàn)錯誤問題
解決辦法:線程同步
線程等待(join)
互斥鎖:對共享數(shù)據(jù)進行鎖定抖苦,保證同一時刻只能有一個線程去操作。搶到鎖的線程先執(zhí)行膛腐,沒有搶到的需要等待睛约。
threading模塊中定義了Lock變量,這個變量本質(zhì)上是一個函數(shù)哲身,通過調(diào)用這個函數(shù)可以獲取一把互斥鎖辩涝。
互斥鎖使用步驟:
# 創(chuàng)建鎖
mutex = threading.Lock()
# 上鎖
mutex.acquire()
...這里編寫代碼能保證同一時刻只能有一個線程去操作, 對共享數(shù)據(jù)進行鎖定...
# 釋放鎖
mutex.release()
注意點:
- acquire和release方法之間的代碼同一時刻只能有一個線程去操作
- 如果在調(diào)用acquire方法的時候 其他線程已經(jīng)使用了這個互斥鎖,那么此時acquire方法會堵塞勘天,直到這個互斥鎖釋放后才能再次上鎖怔揩。
import threading
# 定義全局變量
g_num = 0
# 創(chuàng)建全局互斥鎖
lock = threading.Lock()
# 循環(huán)一次給全局變量加1
def sum_num1():
# 上鎖
lock.acquire()
for i in range(1000000):
global g_num
g_num += 1
print("sum1:", g_num)
# 釋放鎖
lock.release()
# 循環(huán)一次給全局變量加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__':
# 創(chuàng)建兩個線程
first_thread = threading.Thread(target=sum_num1)
second_thread = threading.Thread(target=sum_num2)
# 啟動線程
first_thread.start()
second_thread.start()
# 提示:加上互斥鎖捉邢,那個線程搶到這個鎖我們決定不了,那線程搶到鎖那個線程先執(zhí)行商膊,沒有搶到的線程需要等待
# 加上互斥鎖多任務瞬間變成單任務伏伐,性能會下降,也就是說同一時刻只能有一個線程去執(zhí)行
總結(jié)
- 和計算密集型的相關操作使用多進程晕拆;如果IO密集型藐翎,使用多線程
- 多進程可以使用多核,資源開銷大实幕;而多線程不可以吝镣,資源開銷小
- 多進程穩(wěn)定性強,因為進程之間相互不影響