文章目錄
進程
多任務
操作系統上運行多個任務同時運行
並行:
任務的數量小於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)