python進階(八)~~~隊列和多進程


一、隊列

Python的Queue模塊中提供了同步的、線程安全的隊列類,包括FIFO(先入先出)隊列QueueLIFO(后入先出)隊列LifoQueue,和優先級隊列PriorityQueue。這些隊列都實現了鎖原語,能夠在多線程中直接使用。可以使用隊列來實現線程間的同步。

queue.Queue(maxsize=0)  maxsize默認為0,不設置或設置為負數時,表示可接受的消息數量沒有上限。

常用方法:

Queue.task_done() 在完成一項工作之后,Queue.task_done()函數向任務已經完成的隊列發送一個信號。每個get()調用得到一個任務,接下來task_done()調用告訴隊列該任務已經處理完畢。

Queue.join()       實際上意味着等到隊列為空,再執行別的操作

Queue.put(item,block=True, timeout=None)     寫入隊列,block=True,Timeout=3 意味着等待3s,隊列仍沒有位置就報錯;block=False 意思是不等待,隊列如果滿了直接報錯;

Queue.get(block=True, timeout=None)    獲取隊列,block和timeout參數說明同上put

Queue.get_nowait()       相當於Queue.get(block=False),不等待

Queue.put_nowait()       相當於Queue.put(block=False),不等待

Queue.qsize()     返回隊列的大小

Queue.empty()    如果隊列為空,返回True,反之False

Queue.full()         如果隊列滿了,返回True,反之False,Queue.full 與 maxsize 大小對應

from  queue import Queue,LifoQueue,PriorityQueue

#先入先出隊列
q1=Queue(masize=2)

q1.put(1)
q1.put(2)
print(q1.full())  # 判斷隊列是否已滿,返回True
print(q1.qsize())  # 返回當前隊列元素的個數
q1.put(3,block=False)  # 向隊列中加入值,不等待,直接報錯。 
q1.get()
q1.get()
print(q1.empty())  # 判斷隊列是否為空,返回True

q1.task_done()
q1.join()  #等待隊列任務全部完成再繼續執行,與隊列長度無關,隊列中每一個任務執行完需調用task_done方法,所有任務完成后才會繼續執行join()后面的代碼

使用隊列解決100萬的bug:耗時比加鎖長

from threading import Thread,Lock
from queue import Queue

q=Queue(maxsize=2)
num=0
q.put(num)

def work1():
    global num
    for i in range(1000000):
        q.get()  #讀取隊列中的值
        num+=1    
        q.put(num)  #加入隊列

def work2():
  global num
    for i in range(1000000):
        q.get()
        num+=1    
        q.put(num)  #加入隊列

def main():
    t1=Thread(target=work1)
    t2=Thread(target=work2)
    t1.start()
    t2.start()
    t1.join()
    t2.join()
    print(num)

if __name__=='__main__':
    main()

LifoQueue 后入先出隊列: 繼承了Queue,所有方法用法與Queue一樣,僅僅是取出的順序發生變化。 

PriorityQueue 優先級隊列: q=PriorityQueue ()     q.put((1,22))  以元祖形式添加元素,前面為優先級,后面為元素。優先級越低,越先取出。


二、進程

1.什么是進程?

一個程序運行起來后,代碼+用到的資源 稱之為進程,是系統進行資源分配和調度的基本單位,一個程序至少有一個進程。在當代面向線程設計的計算機結構中,進程是線程的容器。程序是指令、數據及其組織形式的描述,進程是程序的實體。我們自己在python文件中寫了一些代碼,這叫做程序,運行這個python文件的時候,這叫做進程。

2.進程狀態介紹

  在了解其他概念之前,我們首先要了解進程的幾個狀態。在程序運行的過程中,由於被操作系統的調度算法控制,程序會進入幾個狀態:就緒,運行和阻塞。

  (1)就緒(Ready)狀態

    當進程已分配到除CPU以外的所有必要的資源,只要獲得處理機便可立即執行,這時的進程狀態稱為就緒狀態。

  (2)執行/運行(Running)狀態當進程已獲得處理機,其程序正在處理機上執行,此時的進程狀態稱為執行狀態。

  (3)阻塞(Blocked)狀態正在執行的進程,由於等待某個事件發生而無法執行時,便放棄處理機而處於阻塞狀態。引起進程阻塞的事件可有多種,例如,等待I/O完成、申請緩沖區不能滿足、等待信件(信號)等。

    事件請求:input、sleep、文件輸入輸出、recv、accept等

    事件發生:sleep、input等完成了

    時間片到了之后有回到就緒狀態,這三個狀態不斷的在轉換。

3.進程,線程對比

  功能:

  • 進程,能夠完成多任務,比如 在一台電腦上能同時運行多個軟件

  • 線程,能夠完成多任務,比如一個QQ中的多個聊天窗口

  定義:

  • 進程是系統進行資源分配和調度的一個獨立單位

  • 線程是進程的一個實體,是CPU調度和分配的基本單位,它是比進程更小的能獨立的基本單位,線程自己基本上不擁有系統資源,只擁有一點在運行中必不可少的資源(如程序計數器,一組寄存器和棧),但是它可與同屬一個進程的其他線程共享所擁有的全部資源

  區別:

  • 一個程序至少有一個進程,一個進程至少有一個線程

  • 線程的划分尺度小於進程(資源比進程少),使得多線程程序並發性高

  • 進程在執行過程中擁有獨立的內存單元,而多個線程共享內存,從而極大地提高了程序的運行效率

  • 線程不能夠獨立執行,必須依存在進程中

  • 可以將進程理解為工廠中的一條流水線,而線程就是流水線上的工人

  優缺點:

  • 線程執行開銷小,但不利於資源的管理和保護;進程正相反

from multiprocessing import Process
import time

def work1():
   for i in range(6):
        time.sleep(1)
        print(f'第{i+1}次澆花中')

def work2(name):
   for i in range(5):
        time.sleep(1)
        print(f'第{i+1}次{name}打牆中')

#windows下不能直接像下面這樣引用,新建進程時會先import 進程.py原文件---執行--導入--執行--,陷入無限遞歸中報錯。
# 一定要在main下面使用,這樣即使導入進程腳本,也會因為運行的不是原腳本文件而不再繼續執行
#linux/mac上不會報錯,引入機制不同。
# p1=Process(target=work2,args=('musen',))
# p1.start()
# work1()

def main():
    t1 = Process(target = work2 , args = ('musen' ,))  # Thread(target=work2,kwargs={'name':'musen'}) 這種方式傳參
    t1.start()
    work1()
    
#單任務,多進程
if __name__ == '__main__':
    main()

創建進程類

from multiprocessing import Process

class MyProcess(Process):
    def run(self):
        with open('1.txt','a',encoding = 'utf8') as f:
            for i in range(2):
                f.write('python\n')

def main():
    p_list=[]
    for i in range(3):
        p=MyProcess()
        p.start()   
    for j in p_list:
        j.join()

if __name__=="__main__":
    main()

進程參數:

p=multiprocessing.Process(group=None, target=None, name=None, args=(), kwargs={}, daemon=None)

  1. group 參數未使用,值始終為None,大部分情況用不到
  2. target 表示調用對象,即子進程要執行的任務
  3. args 表示調用對象的位置參數元組,args=(1,2,'egon',)
  4. kwargs 表示調用對象的字典,kwargs={'name':'egon','age':18}
  5. name 為子進程的名稱

Process創建的實例方法:

  1. p.start():啟動進程,並調用該子進程中的p.run()
  2. p.run(): 進程啟動時運行的方法,正是它去調用target指定的函數,我們自定義類的類中一定要實現該方法
  3. p.terminate(): 強制終止子進程p,不會進行任何清理操作,如果p創建了子進程,該子進程就成了僵屍進程,使用該方法需要特別小心這種情況。如果p還保存了一個鎖那么也將不會被釋放,進而導致死鎖
  4. p.is_alive(): 判斷進程是否活着,如果p仍然運行,返回True
  5. p.join([timeout]): 是否等待子進程結束,(強調:是主進程處於等的狀態,而p是處於運行的狀態)。timeout是可選的超時時間,需要強調的是,p.join只能join住start開啟的進程,而不能join住run開啟的進程

Process創建的實例屬性:

  1. p.daemon:默認值為False,如果設為True,代表p為后台運行的守護進程,當p的父進程終止時,p也隨之終止,並且設定為True后,p不能創建自己的新進程,必須在p.start()之前設置
  2. p.name:進程的名稱,默認為Process-N,N為從1開始遞增的整數
  3. p.pid:進程的pid,進程號,也可以通過os.getpid()獲取當前進程號
  4. p.exitcode:進程在運行時為None、如果為–N,表示被信號N結束(了解即可)
  5. p.authkey:進程的身份驗證鍵,默認是由os.urandom()隨機生成的32字符的字符串。這個鍵的用途是為涉及網絡連接的底層進程間通信提供安全性,這類連接只有在具有相同的身份驗證鍵時才能成功(了解即可)

多進程之間不共享全局變量

import os
from multiprocessing import Process

num=0

def work1():
    global num
    for i in range(100000):
        num+=1
    print(F'進程號{os.getpid()}',num)

def work2():
    global num
    for i in range(100000):
        num+=1
    print(F'進程號{os.getpid()}',num)

def main():
    p1=Process(target=work1)
    p2=Process(target=work2)
    p1.start()
    p2.start()
    p1.join()
    p2.join()
    print('主進程num值',num)

if __name__=='__main__':
    main()  #返回 進程號11844 100000,   進程號2224 100000,主進程num值 0

多進程爭奪共有資源讀寫操作時,會產生覆蓋。為了避免這種情況,使進程串行處理。需要加鎖

from multiprocessing import Process,Lock

def work3(metux):
    metux.acquire()
    with open('1.txt' , 'a' , encoding = 'utf8') as f :
        for i in range(3) :
            f.write('python\n')
    metux.release() def work4(metux):
    metux.acquire()
    with open('1.txt' , 'a' , encoding = 'utf8') as f :
        for i in range(3) :
            f.write('java\n')
    metux.release() def main():
    metux=Lock()
    p1=Process(target=work3,args = (metux,))
    p2=Process(target=work4,args = (metux,))
    p1.start()
    p2.start()
    p1.join()
    p2.join()

if __name__=='__main__':
    main() 

進程之間的通信:使用隊列

queue模塊的隊列:適用於同一進程中,各線程之間的通信

multiprocessing進程模塊的隊列:適用於不同進程之間的通信。注意點:需要通過參數傳入進程類中。

import os
from multiprocessing import Process,Queue q=Queue() for i in range(5): q.put(i) def work1(q): while not q.empty(): print('work1--------',os.getpid(),q.get()) def work2(q): while not q.empty() : print('work2--------',os.getpid(),q.get()) def main(): p1=Process(target=work1,args = (q,)) p2=Process(target=work2,args = (q,)) p1.start() p2.start() if __name__=='__main__': main()

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM