python 進程 線程 總結

進程

多任務

操作系統上運行多個任務同時運行

並行:

任務的數量小於cpu的核心數量【理想型】

併發:

任務的數量大於cpu的核心數量。【現實】

單核cpu運行

操作系統處理方式:每個任務輪流交替執行,由於不同的任務之間的切換速度很快,導致肉眼無法識別

進程

一個任務即一個進程【Process】。
exp:打開一個瀏覽器。打開一個word啓動一個word進程。但是,一個進程可以幹很多事情(讀、寫等等),在一個進程的內部,要同時幹很多件事情,就需要同時執行多個子任務,將一個進程中的多個子任務被稱爲線程【Thread】

進程的特點

a、獨立性:不同的進程之間是相互獨立的,在操作系統中互不影響,相互之間資源不共享
b、動態性:進程一旦被啓動之後,在操作系統中並不是靜止不動的,一直處於動態狀態
c、併發性:在操作系統交替執行
多任務的實現方式:

多任務的實現方式

a、多進程模式:啓動多個進程,每個進程雖然只有一個線程,通過改線程執行一個任務
b、多線程模式:啓動一個進程,改改進程啓動多個線程,每個線程執行一個任務
c、協程模式:yield【函數生成器】
d、多進程模式+多線程模式:啓動多個進程,每個進程啓動多個線程,執行的任務的數量會更多

multiprocessing模塊(多進程模塊)

Process

通過Process創建對象實現多進程

import time
import os
from multiprocessing import Process


def son():
    # os,getpid 顯示當前進程  系統編號
    print("子進程開始了:%s\n" % os.getpid())
    time.sleep(1)
    print("子進程幹活了\n")
    time.sleep(1)
    print("子進程結束了:%s\n" % os.getpid())
    time.sleep(2)


if __name__ == '__main__':
    print("主進程開始:%s\n" % os.getpid())

    # 創建子進程對象
    p = Process(target=son)
    # 啓動子進程
    p.start()
    # p.join() # 可以修改進程執行順序

    print("主進程結束了")

"""
主進程開始:7504

主進程結束了
子進程開始了:9692

子進程幹活了

子進程結束了:9692

"""

"""
從結果上看出,執行的main和son()的是兩個進程,
如果沒有創建進程,直接執行son則線程號是一樣的,
從上而下依次執行

p.join():修改執行順序,父進程等待子進程結束後在繼續執行

如果son有參數,則創建的時候也是需要以元組的形式傳入。可以參考下面的進程池。


"""

Pool

pool:進程池 如果我們需要開啓多個進程,可以通過Process一個個的創建,只是這樣有點麻煩,Python內置了Pool這個類(object),用來創建多個進程,簡化代碼。
!注意:使用pool的時候先查看自己電腦是幾核的(cpu內核數量,不懂自行百度),可以開啓幾個進程。

import time
import os
from multiprocessing import Pool


def run(mag):
    # for _ in range(5):
    print("Nice day~~~進程號:%s" % os.getpid())
    time.sleep(1)


if __name__ == '__main__':
    print("主進程開始~~~%s" % os.getpid())
    time.sleep(1)
    p = Pool(4)        # 開啓 4 個進程
    for i in range(8): # 任務執行8次,
        p.apply_async(run, args=(i,)) # 執行任務

    p.close()
    p.join()

    print("主進程結束~~~%s" % os.getpid())

"""
p = pool(4) 這裏表示我們允許(需要執行的函數)最多佔用4個進程
p.apply_async(run,args=(i,))  調用Pool的方法,參數爲一個函數,即run
args=(i,)是run函數需要的參數,必須以元組的形式傳入。
p.close()表示待循環完成以後關閉進程池
p.join() 表示子進程要執行,類似於Process的start(),開啓進程池並且改變順序。
等待子進程全部執行完畢,主進程繼續。
"""

"""
主進程開始~~~10404
Nice day~~~進程號:9180
Nice day~~~進程號:11740
Nice day~~~進程號:7112
Nice day~~~進程號:4508
Nice day~~~進程號:11740
Nice day~~~進程號:9180
Nice day~~~進程號:7112
Nice day~~~進程號:4508
主進程結束~~~10404
"""

封裝進程對象

import time
import os
from multiprocessing import Pool, Process


class Defined(Process):
    def run(self) -> None:   # "->" 在python中叫“註解”,自行百度
        print("子進程開始:%s" % os.getpid())
        time.sleep(2)
        print("子進程結束:%s" % os.getpid())


if __name__ == '__main__':
     print("主進程啓動:%s" % os.getpid())
     p = Defined()
     p.start()
     p.join()

     print("結束%s" % os.getpid())
"""
主進程啓動:10780
子進程開始:11096
子進程結束:11096
結束10780
"""

進程間的通信

Process之間是需要相互通信的,操作系統提供了很多的辦法實現進程間的通信,Python中的multiprocessing包裝了底層的機制,提供了Queue【隊列】,Pipes等多種方式實現交換數據

#需求:在主進程中創建兩個子進程,一個進程用來向隊列中寫數據,另外一個進程用來從隊列中讀數據

from  multiprocessing import  Process,Queue
import  os,time,random

#1.寫數據的任務
def write(queue):
    print("進程%s開始" % (os.getpid()))
    print("開始寫入")

    for value in ["A","B","C"]:
        print("add %s to queue" % (value))

        #向隊列中添加數據
        queue.put(value)

        #稍休息片刻,接着加
        time.sleep(random.random())

    print("進程%s結束" % (os.getpid()))

#2.讀數據的任務
def read(queue):
    print("進程%s開始" % (os.getpid()))
    print("開始讀取")

    n = 0
    while n < 3:
        #從隊列中獲取數據
        value = queue.get(True)
        print("get %s from queue" % (value))
        n += 1

    print("進程%s結束" % (os.getpid()))

#3.在主進程中分別創建子進程,執行相應的任務
if __name__ == "__main__":
    print("父進程啓動")

    #3.1創建隊列對象,並傳參給每個進程
    q = Queue()

    #3.2創建進程對象,
    pr = Process(target=read,args=(q,))
    pw = Process(target=write,args=(q,))

    #3.3啓動子進程,讓進行數據的讀寫
    pw.start()
    pr.start()

    #3.4設置,讓所有子進程結束之後,才結束父進程
    pr.join()
    pw.join()

    print("父進程結束")

線程

of:是進程的組成部分,一個進程可以有多個線程,每個線程去處理一個特定的子任務
注意:一個進程至少有一個子任務,否則改進程沒有意義

線程的執行是搶佔式的,多個線程在同一個進程中可以併發執行,其實就是在cpu之間進行的快速的切換【每個線程都有爭搶時間片的機會,誰搶到時間片,則執行對應的線程,剩下的線程都會被掛起】
比如: 打開網易雲----》 啓動了一個進程
   播放歌曲和刷新歌詞-----啓動了兩個線程
  
【面試題】進程和線程的關係
a、一個程序啓動以後至少有一個進程
b、一個進程可以包含多個線程,但是至少需要一個線程,否則改進程沒有意義
c、一個進程可以包含多個線程,線程之間是可以資源共享的
d、系統創建進程需要爲該進程重新分配資源,而創建線程容易的多,因此使用多線程實現多任務比多進程實現效率更高。

創建線程

_thread模塊:提供了低級別的,原始的線程【功能有限,底層採用的是c語言】
threading模塊:高級模塊,對_thread進行了封裝,有_thread沒有的功能

創建多線程

import time
import threading

def run():
    # for _ in range(5):
        print("Nice_day")
        time.sleep(1)


if __name__ == '__main__':
    se = threading.BoundedSemaphore(5)  # 有沒有都一樣,限制最大線程數爲5
    t_list = []
    for i in range(5):
        t = threading.Thread(target=run)
        t_list.append(t)               # 將創建的線程保存在t_list中

    for j in t_list:
        j.start()              
    # run()
    print("主線程結束")
"""
代碼解釋:
單線程執行是一個執行完再接着執行下一個,如run()註釋掉的for語句,一次接着一次打印。
如果我們同時創建多個線程,則一次性執行5次run()。
"""

線程鎖

原因

線程的執行是搶佔式的,多個線程在同一個進程中可以併發執行,其實就是在cpu之間進行的快速的切換【每個線程都有爭搶時間片的機會,誰搶到時間片,則執行對應的線程,剩下的線程都會被掛起】

from  threading import  Thread,Lock

balance = 0

#創建一個鎖對象
lock = Lock()

def change(n):
    global balance
    balance = balance + n
    balance = balance - n

#子線程的任務
def run(n):
    for _ in range(1000000):
        #獲取鎖
        lock.acquire()

        try:
            # 臨界資源
            change(n)
        finally:
            #釋放鎖
            lock.release()


if __name__ == "__main__":

    t1 = Thread(target=run,args=(5,))
    t2 = Thread(target=run,args=(8,))

    t1.start()
    t2.start()

    t1.join()
    t2.join()

    #理論上,balance最終的結果爲0
    print(balance)
from  threading import  Thread,Lock

balance = 0

#創建一個鎖對象
lock = Lock()

def change(n):
    global balance
    balance = balance + n
    balance = balance - n

#子線程的任務
def run(n):
    for _ in range(1000000):
        #同樣表示獲取鎖,但是,不用手動釋放,當臨界資源的代碼執行完畢之後,鎖會被自動釋放
        with lock:
            # 臨界資源
            change(n)

if __name__ == "__main__":

    t1 = Thread(target=run,args=(5,))
    t2 = Thread(target=run,args=(8,))

    t1.start()
    t2.start()

    t1.join()
    t2.join()

    #理論上,balance最終的結果爲0
    print(balance)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章