PythonStudy——多進程編程

進程

一個正在被運行的程序就稱之爲進程,是程序具體執行過程,一種抽象概念python

進程來自於操做系統 linux

多進程

進程和程序的區別

程序就是一堆計算機能夠識別文件,程序在沒有被運行就是躺在硬盤上的一堆二進制程序員

運行程序時,要從硬盤讀取數據到內存中,CPU從內存讀取指令並執行 ,編程

一旦運行就產生了進程 瀏覽器

一個程序能夠屢次執行 產生多個進程,可是進程之間相互獨立安全

當咱們右鍵運行了一個py文件時 ,其實啓動的是python解釋器,你的py文件實際上是看成參數傳給瞭解釋器 網絡

阻塞 非阻塞 並行 併發 (重點)

阻塞 : 程序遇到io操做是就進入了阻塞狀態 數據結構

本地IO input print sleep read write 併發

網絡IO recv senddom

非阻塞: 程序正常運行中 沒有任何IO操做 就處於非阻塞狀態

阻塞 非阻塞 說的是程序的運行狀態

併發: 多個任務看起來同時在處理 ,本質上是切換執行 速度很是快

並行: 多個任務真正的同時執行 必須具有多核CPU 纔可能並行

併發 並行 說的是 任務的處理方式

三種狀態的切換

程序員永恆的話題

提升效率

根本方法就是讓程序儘量處於運行狀態

減小IO 儘量多佔用CPU時間

緩衝區就是用於減小IO操做的

進程的建立以及銷燬 瞭解

菜譜: 就是程序

作菜的過程:就是進程

進程的兩種使用方式 (重點)

1.直接實例化Process ,將要執行任務用target傳入

 

2.繼承Process類 ,覆蓋run方法 將任務放入run方法中

import os
from multiprocessing import  Process
class MyProcess(Process):
   def __init__(self,name):
       super().__init__()
       self.name = name
   # 繼承Procee覆蓋run方法將要執行任務發到run中
   def run(self):
       print(self.name)
       print("子進程 %s running!" % os.getpid())
       print("子進程 %s over!" % os.getpid())

if __name__ == '__main__':
   # 建立時 不用再指定target參數了
   p = MyProcess("rose")
   p.start()
   print("父進程over!")

join函數 (重點)

Process的對象具有一個join函數

用於提升子進程優先級 ,使得父進程等待子進程結束

殭屍與孤兒進程 瞭解

孤兒進程

指的是,父進程先結束 ,而子進程還在運行着,

孤兒進程無害,有 其存在的必要性

例如:qq開啓了瀏覽器,qq先退出了 瀏覽器應該繼續運行

孤兒進程會被操做系統接管

殭屍進程

值得是,子進程已經結束了,可是操做系統會保存一些進程信息,如PID,運行時間等,此時這個進程就稱之爲殭屍進程

殭屍進程若是太多將會佔用大量的資源,形成系統沒法開啓新新進程

linux 中有一個wai/waitpid 用於父進程回收子進程資源

python會自動回收殭屍進程

經常使用屬性

# p.join() # 等待子進程結束
# p.terminate() # 終止進程
# print(p.name) # 進程的名稱
# print(p.is_alive()) #是否存活
# p.terminate() # 與start同樣 都是給操做系統發送指令 因此會有延遲
# print(p.is_alive())
# print(p.pid)
# print(p.exitcode) # 獲取退出碼

守護進程

    什麼是守護進程

​    進程是一個正在運行的程序

​    守護進程也是一個普通進程

​    意思是一個進程能夠守護另外一個進程

例如

​ 康熙要是一個進程的話,後宮佳麗都是守護者

​ 若是康熙掛了, 後宮佳麗們要陪葬

結論:

若是b是a的守護進程,a是被守護的進程,a要是掛了,b也就隨之結束了

測試:

from multiprocessing import Process import time # 妃子的一輩子
def task(): print("入宮了.....") time.sleep(50) print("妃子病逝了......") if __name__ == '__main__': # 康熙登基了
print("登基了.....") # 找了一個妃子
p = Process(target=task) # 設置爲守護進程 必須在開啓前就設置好
p.daemon = True p.start() # 康熙駕崩了
time.sleep(3) print("故事結束了!")

使用場景:

​ 父進程交給了子進程一個任務,任務尚未完成父進程就結束了,子進程就沒有繼續執行的意義了

​ 例如:qq 接收到一個視頻文件,因而開啓了一個子進程來下載,若是中途退出了qq,下載任務就沒必需要繼續運行了

 

互斥鎖

什麼是互斥鎖

互斥鎖 互相排斥的鎖,我在這站着你就別過來,(若是這個資源已經被鎖了,其餘進程就沒法使用了)

須要強調的是: 鎖 並非真的把資源鎖起來了,只是在代碼層面限制你的代碼不能執行

爲何須要互斥鎖:

併發將帶來資源的競爭問題
當多個進程同時要操做同一個資源時,將會致使數據錯亂的問題
以下列所示:

解決方案1:

​ 加join,
​ 弊端 1.把本來併發的任務變成了穿行,避免了數據錯亂問題,可是效率下降了,這樣就不必開子進程了
​ 2.本來多個進程之間是公平競爭,join執行的順序就定死了,這是不合理的

解決方案2:

​ 就是給公共資源加鎖,互斥鎖
​ 互斥鎖 互相排斥的鎖,我在這站着你就別過來,(若是這個資源已經被鎖了,其餘進程就沒法使用了)

鎖 並非真的把資源鎖起來了,只是在代碼層面限制你的代碼不能執行

鎖和join的區別:​

1. join是固定了執行順序,會形成父進程等待子進程

​ 鎖依然是公平競爭誰先搶到誰先執行,父進程能夠作其餘事情

​ 2.最主要的區別:
​ join是把進程的任務所有串行
​ 鎖能夠鎖任意代碼 一行也能夠 能夠本身調整粒度


案例

from multiprocessing import Process,Lock import time,random def task1(lock): # 要開始使用了 上鎖
lock.acquire() #就等同於一個if判斷
print("hello iam jerry") time.sleep(random.randint(0, 2)) print("gender is boy") time.sleep(random.randint(0, 2)) print("age is 15") # 用完了就解鎖
lock.release() def task2(lock): lock.acquire() print("hello iam owen") time.sleep(random.randint(0,2)) print("gender is girl") time.sleep(random.randint(0,2)) print("age is 48") lock.release() def task3(lock): lock.acquire() print("hello iam jason") time.sleep(random.randint(0,2)) print("gender is women") time.sleep(random.randint(0,2)) print("age is 26") lock.release() if __name__ == '__main__': lock = Lock() p1 = Process(target=task1,args=(lock,)) p2 = Process(target=task2,args=(lock,)) p3 = Process(target=task3,args=(lock,)) p1.start() # p1.join()
 p2.start() # p2.join()
 p3.start() # p3.join()

# print("故事結束!")

# 鎖的僞代碼實現

# if my_lock == False: # my_lock = True # #被鎖住的代碼
my_lock = False 解鎖

注意1: 不要對同一把執行多出acquire 會鎖死致使程序沒法執行 一次acquire必須對應一次release

l = Lock()
l.acquire()
print("搶到了!")
l.release()
l.acquire()
print("強哥毛線!")

注意2:想要保住數據安全,必須保住全部進程使用同一把鎖

 

IPC

​ 進程間通信

​ 通信指的就是交換數據

​ 進程之間內存是相互隔離的,當一個進程想要把數據給另一個進程,就須要考慮IPC

方式

​ 管道

       只能單向通信,數據都是二進制

​ 文件

       在硬盤上建立共享文件

​ 缺點:速度慢

​ 優勢:數據量幾乎沒有限制

​socket

​ 編程複雜度較高

​ 共享內存:必須由操做系統來分配 要掌握的方式*****

​ 優勢: 速度快

​ 缺點: 數據量不能太大

共享內存的方式

Manager類

​ Manager提供不少數據結構 list dict等等

​ Manager所建立出來的數據結構,具有進程間共享的特色

from multiprocessing import Process,Manager,Lock import time def task(data,l): l.acquire() num = data["num"] # time.sleep(0.1) data["num"] = num - 1 l.release() if __name__ == '__main__': # 讓Manager開啓一個共享的字典
m = Manager() data = m.dict({"num":10}) l = Lock() for i in range(10): p = Process(target=task,args=(data,l)) p.start() time.sleep(2) print(data)

​ 須要強調的是 Manager建立的一些數據結構是不帶鎖的 可能會出現問題

Queue隊列

幫咱們處理了鎖的問題 重點

​ 隊列是一種特殊的數據結構,先存儲的先取出 就像排隊 先進先出

​ 相反的是堆棧,先存儲的後取出, 就像衣櫃 桶裝薯片 先進後出

​ 擴展:

​ 函數嵌套調用時 執行順序是先進後出 也稱之爲函數棧

​ 調用 函數時 函數入棧 函數結束就出棧

from multiprocessing import Queue # 建立隊列 不指定maxsize 則沒有數量限制
q = Queue(3) # 存儲元素 # q.put("abc") # q.put("hhh") # q.put("kkk")

# print(q.get()) # q.put("ooo") # 若是容量已經滿了,在調用put時將進入阻塞狀態 直到有人從隊列中拿走數據有空位置 纔會繼續執行

#取出元素 # print(q.get())# 若是隊列已經空了,在調用get時將進入阻塞狀態 直到有人從存儲了新的數據到隊列中 纔會繼續

# print(q.get()) # print(q.get())


#block 表示是否阻塞 默認是阻塞的 # 當設置爲False 而且隊列爲空時 拋出異常
q.get(block=True,timeout=2) # block 表示是否阻塞 默認是阻塞的 # 當設置爲False 而且隊列滿了時 拋出異常 # q.put("123",block=False,) # timeout 表示阻塞的超時時間 ,超過期間仍是沒有值或仍是沒位置則拋出異常 僅在block爲True有效

 

 

生產者消費者模型 重點

## 是什麼

​ 模型 就是解決某個問題套路

​ 產生數據的一方稱之爲生產者

​ 處理數據的一方稱之爲消費者

​ 例如: 爬蟲 生活中處處都是這種模型

​ 飯店 廚師就是生產者 你吃飯的人就是消費者

## 生產者和消費者出啥問題了? 解決什麼問題

​ 生產者和消費,處理速度不平衡,一方快一方慢,致使一方須要等待另外一方

## 生產者消費者模型解決這個問題的思路: 怎麼解決

​ 本來,雙方是耦合 在一塊兒,消費必須等待生產者生成完畢在開始處理, 反過來

​ 若是消費消費速度太慢,生產者必須等待其處理完畢才能開始生成下一個數據

### 解決的方案:

​ 將雙方分開來.一專門負責生成,一方專門負責處理

​ 這樣一來數據就不能直接交互了 雙方須要一個共同的容器

​ 生產者完成後放入容器,消費者從容器中取出數據

​ 這樣就解決了雙發能力不平衡的問題,作的快的一方能夠繼續作,不須要等待另外一方

案例:

def eat(q): for i in range(10): # 要消費
rose = q.get() time.sleep(random.randint(0, 2)) print(rose,"吃完了!") # 生產任務
def make_rose(q): for i in range(10): # 再生產
time.sleep(random.randint(0, 2)) print("第%s盤青椒肉絲製做完成!" % i) rose = "第%s盤青椒肉絲" % i # 將生成完成的數據放入隊列中
q.put(rose) if __name__ == '__main__': # 建立一個共享隊列
q = Queue() make_p = Process(target=make_rose,args=(q,)) eat_p = Process(target=eat,args=(q,)) make_p.start() eat_p.start()
相關文章
相關標籤/搜索