python 筆記二

 17.進程線程python

 進程間通訊方式:管道Pipe;隊列Queue;共享內存Value、Array、Manager;安全

 多進程同步:鎖Lock、遞歸鎖RLock、Condition(條件變量);事件event;信號signal信號量Semaphore;app

  • 函數方式
from multiprocessing import Process
import time
import os


def test(sub_p):
    for j in range(20):
        print('%s----pid=%s' % (sub_p, os.getpid()))
        time.sleep(1)


for i in range(3):
    p = Process(target=test, name='sub_%s' % i, args=(i,))
    print('sub process %s' % p.name)
    p.start()
    p.join(5)  # join表示延時時間,也就是等待子進程的時間,當10秒過了之後,則會運行主進程。


for i in range(60):
    print('----')
    time.sleep(1)
  • 類的方式
from multiprocessing import Process
import time
import os


class ProcessClass(Process):
    def __init__(self, sub_p):
        super(ProcessClass, self).__init__()
        self.sub_p = sub_p

    def run(self):  # 重寫run方法,當調用start方法時,則會默認調用run方法,因此不用再填寫target參數。
        for j in range(20):
            print('%s----pid=%s' % (self.sub_p, os.getpid()))
            time.sleep(1)


for i in range(3):
    p = ProcessClass(i)
    p.start()
    p.join(5)  # 這裏將會等待子進程單獨運行5秒。

for i in range(60):  # 主進程,當join等待結束收,則會父子進程一塊兒運行。可是若是當父進程運行完,子進程尚未結束,那麼父進程會繼續等子進程。
    print('--main--')
    time.sleep(1)
  •  進程池pool
方法 描述
apply() 以同步方式添加進程
apply_async() 以異步方式添加進程
close() 關閉Pool,使其不接受新任務(還可使用)
terminate() 無論任務是否完成,當即終止
join() 主進程阻塞,等待子進程的退出,必須在close和terminate後使用
from multiprocessing import Pool  # 導入Pool模塊類
import os
import time


def work(num):
    print('進程的pid是%d,進程值是%d' % (os.getpid(), num))
    time.sleep(2)


p = Pool(2)  # 實例化對象,參數2表示建立2個子進程,就是說每次只能執行2個進程。

for i in range(6):
    print('--%d--' % i)
    # 向實例對象添加6次任務,就是6個進程,可是實例對象的進程池只有2個,因此每次往進程池中放2個進程,
    # 當進程池中的2個進程執行完之後,再容許向進程池中添加進程。
    p.apply_async(work, (i,))

p.close()  # 關閉進程池,再也不接收進程任務。
p.join()  # 當子進程工做結束後,則會運行主進程。
  • 進程間通訊:Queue消息隊列
方法  描述
put q.put(數據),放入數據(如隊列已滿,則程序進入阻塞狀態,等待隊列取出後再放入)
put_nowait q.put_nowati(數據),放入數據(如隊列已滿,則不等待隊列取出後再放入,直接報錯)
get q.get(數據),取出數據(如隊列爲空,阻塞等待隊列放入數據後再取出)
get_nowait q.get_nowait(數據),取出數據(如隊列爲空,不等待隊列取出以前數據,直接報錯),放入數據後立馬判斷是否爲空有時爲True,緣由是放入值和判斷同時進行
qsize q.qsize() 消息數量
empty q.empty() 返回True或False,判斷是否爲空
full q.full() 返回值爲True或False,判斷是否爲滿
  • 進程間通訊:Pipe管道
     
from multiprocessing import Process, Pipe def f(conn): conn.send([1, 'test', None]) conn.send([2, 'test', None]) print('child get: %s' % conn.recv())  # 沒數據時讀阻塞
 conn.close() if __name__ == "__main__": # Pipe(duplex=True)返回管道的兩端,duplex=True時雙向管道;False時單向parent_conn只讀,child_conn只寫
    parent_conn, child_conn = Pipe(duplex=True) p = Process(target=f, args=(child_conn,)) p.start() res = parent_conn.recv() print('parent get: %s, type=%s' % (res, type(res))) print('parent get: %s' % parent_conn.recv()) parent_conn.send('father test') p.join()
  • 進程間通訊,共享數據:value+array, Manager

      Python中進程間共享數據,處理基本的queue,pipe和value+array外,還提供了更高層次的封裝。使用multiprocessing.Manager能夠簡單地使用這些高級接口。異步

      Manager()返回的manager對象控制了一個server進程,此進程包含的python對象能夠被其餘的進程經過proxies來訪問。從而達到多進程間數據通訊且安全。async

      Manager支持的類型有list,dict,Namespace,Lock,RLock,Semaphore,BoundedSemaphore,Condition,Event,Queue,Value和Array。函數

from multiprocessing import Manager, Process, Lock


def work(d, lock):
    with lock:  # 不加鎖而操做共享的數據,確定會出現數據錯亂
        d['count'] -= 1


if __name__ == '__main__':
    lock = Lock()
    with Manager() as m:
        dic = m.dict({'count': 100})
        p_l = []
        for i in range(100):
            p = Process(target=work, args=(dic, lock))
            p_l.append(p)
            p.start()
        for p in p_l:
            p.join()
        print(dic)

注意:spa

    使用Manager能夠方便的進行多進程數據共享,但當使用Manager處理list、dict等可變數據類型時,須要很是注意一個陷阱。看下面的代碼:線程

from multiprocessing import Process, Manager

manager = Manager()
m = manager.list()
m.append({'id':1})

def test():
    m[0]['id'] = 2

p = Process(target=test)
p.start()
p.join()
print(m[0])

執行結果是:{'id': 1}不是預期的:{'id': 2}要達到預期的結果,代碼應改成:code

from multiprocessing import Process, Manager

manager = Manager()
m = manager.list()
m.append({'id':1})

def test():
    hack = m[0]
    hack['id'] = 2
    m[0] = hack

p = Process(target=test)
p.start()
p.join()
print(m[0])

以上代碼中讓人困惑的操做的目的是繞過Manager的一個隱祕問題,這個問題是指:Manager對象沒法監測到它引用的可變對象值的修改,須要經過觸發__setitem__方法來讓它得到通知
server

相關文章
相關標籤/搜索