python實現(xiàn)多進程和多線程

進程

程序運行中會有一個默認的進程,如果想要創(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ù))
  1. 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()
  1. 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é)束填帽,有兩種方法:

  1. 讓子進程設置稱為守護主進程蛛淋,主進程退出,子進程立刻銷毀篡腌。此時褐荷,子進程依賴于主進程
if __name__ == '__main__':
    # 創(chuàng)建子進程
    sub_process = multiprocessing.Process(target=task)
    # 設置守護主進程,主進程退出子進程直接銷毀嘹悼,子進程的生命周期依賴與主進程
    sub_process.daemon = True
    sub_process.start()
  1. 退出主進程之前叛甫,將子進程銷毀
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í)行任務傳參

線程注意點

  1. 線程執(zhí)行是無序的
  2. 主線程會等待所有子線程執(zhí)行完成再結(jié)束
# 守護主線程方式1
    sub_thread = threading.Thread(target=show_info, daemon=True)
    # 設置成為守護主線程限匣,主線程退出后子線程直接銷毀不再執(zhí)行子線程的代碼
    # 守護主線程方式2
    # sub_thread.setDaemon(True)
  1. 線程之間共享全局變量
  2. 線程之間共享全局變量數(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)定性強,因為進程之間相互不影響
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末昆庇,一起剝皮案震驚了整個濱河市末贾,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌整吆,老刑警劉巖拱撵,帶你破解...
    沈念sama閱讀 221,635評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異表蝙,居然都是意外死亡拴测,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評論 3 399
  • 文/潘曉璐 我一進店門勇哗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來昼扛,“玉大人,你說我怎么就攤上這事欲诺〕常” “怎么了?”我有些...
    開封第一講書人閱讀 168,083評論 0 360
  • 文/不壞的土叔 我叫張陵扰法,是天一觀的道長蛹含。 經(jīng)常有香客問我,道長塞颁,這世上最難降的妖魔是什么浦箱? 我笑而不...
    開封第一講書人閱讀 59,640評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮祠锣,結(jié)果婚禮上酷窥,老公的妹妹穿的比我還像新娘。我一直安慰自己伴网,他們只是感情好蓬推,可當我...
    茶點故事閱讀 68,640評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著澡腾,像睡著了一般沸伏。 火紅的嫁衣襯著肌膚如雪糕珊。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,262評論 1 308
  • 那天毅糟,我揣著相機與錄音红选,去河邊找鬼。 笑死姆另,一個胖子當著我的面吹牛喇肋,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播迹辐,決...
    沈念sama閱讀 40,833評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼苟蹈,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了右核?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,736評論 0 276
  • 序言:老撾萬榮一對情侶失蹤渺绒,失蹤者是張志新(化名)和其女友劉穎贺喝,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體宗兼,經(jīng)...
    沈念sama閱讀 46,280評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡躏鱼,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,369評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了殷绍。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片染苛。...
    茶點故事閱讀 40,503評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖主到,靈堂內(nèi)的尸體忽然破棺而出茶行,到底是詐尸還是另有隱情,我是刑警寧澤登钥,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布畔师,位于F島的核電站,受9級特大地震影響牧牢,放射性物質(zhì)發(fā)生泄漏看锉。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,870評論 3 333
  • 文/蒙蒙 一塔鳍、第九天 我趴在偏房一處隱蔽的房頂上張望伯铣。 院中可真熱鬧,春花似錦轮纫、人聲如沸腔寡。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,340評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蹬蚁。三九已至恃泪,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間犀斋,已是汗流浹背贝乎。 一陣腳步聲響...
    開封第一講書人閱讀 33,460評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留叽粹,地道東北人览效。 一個月前我還...
    沈念sama閱讀 48,909評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像虫几,于是被迫代替她去往敵國和親锤灿。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,512評論 2 359

推薦閱讀更多精彩內(nèi)容