Python進程、線程、協程

Python進程、線程、協程

線程

​ 基於_thread的threading模塊,提供了更加方便的api來處理進程

import threading
import time

def worker(num):
    time.sleep(1)
    print("The num is %d" %num)
    return

for i in range(20):
    t = threading.Thread(target=worker,args=(i,),name="t.%d"%i)
    t.start()

線程鎖

threading.RLock允許在同一線程中被多次acquire。而threading.Lock卻不允許這種情況,只能單對使用。 如果使用RLock,那麼acquire和release必須成對出現,即調用了n次acquire,必須調用n次的release才能真正釋放所佔用的瑣。

import threading
import time

def test_rlock():
    globals_num = 0
    rlock = threading.RLock()
    # lock = threading.Lock()
    for i in range(10):
        t = threading.Thread(target=test_func,args=(i,rlock,globals_num,),name="t.%d"%i)
        t.start()

def test_func(num,lock,globals_num):
    lock.acquire()
    globals_num += 1
    time.sleep(1)
    print("This thread is %d"%num)
    print("globals_num:",globals_num)
    lock.release()

if __name__ == "__main__":
    test_lock()

線程事件

​ 事件處理的機制:全局定義了一個“Flag”,如果“Flag”值爲 False,那麼當程序執行 event.wait 方法時就會阻塞,如果“Flag”值爲True,那麼event.wait 方法時便不再阻塞。

​ 事件提供了三個方法,wait()設置阻塞狀態;set()設置flagTrueclear設置flagFalse

def test_event():
    event = threading.Event()
    event2 = threading.Event()
    t = threading.Thread(name='t',target=do_event,args=(event,))
    t2 = threading.Thread(name='t2',target=do_event,args=(event2,))
    t.start()
    t2.start()
    print("They are waiting!")
    flag = True
    while flag:
        inp = input("The thread that you want to unlock and input 'exit' to exit : ")
        if inp=='t':
            event.set()
            print("You have unlocked 't' ")
        elif inp == 't2':
            event2.set()
            print("You have unlocked 't2' ")
        elif inp == 'exit':
            break

def do_event(event):
    print("start")
    event.wait()
    print("end")

if __name__ == "__main__":
    test_event()

Condition類

​ Condition類實現了一個conditon變量。 這個conditiaon變量允許一個或多個線程等待,直到他們被另一個線程通知。

​ wati()釋放鎖以後,在被調用相同條件的另一個進程用notify() 或者 notify_all() 叫醒之前 會一直阻塞。如果有等待的線程,notify()方法會喚醒一個在等待conditon變量的線程。notify_all() 則會喚醒所有在等待conditon變量的線程。

def consumer(cond):
    with cond:
        print("consumer before wait")
        cond.wait()
        print("consumer after wait")
   
def producer(cond):
    with cond:
        print("producer before notifyAll")
        cond.notifyAll() #喚醒所有在等待condition變量的線程
        #cond.notify() #喚醒一個在等待condition變量的線程
        print("producer after notifyAll")
   
def test_condition():
    condition = threading.Condition()
    c1 = threading.Thread(name="c1", target=consumer, args=(condition,))
    c2 = threading.Thread(name="c2", target=consumer, args=(condition,))
    
    p = threading.Thread(name="p", target=producer, args=(condition,))
    
    c1.start()
    time.sleep(2)
    c2.start()
    time.sleep(2)
    p.start()
if __name__ == "__main__":
    test_condition()

進程

​ 在multiprocessing中,通過創建Process對象生成進程,然後調用它的start()方法激活進程

from multiprocessing import Process

def create_process():
    p = Process(target=hello,args=("Haihua",))
    p1 = Process(target=hello,args=("WangHaihua",))
    p.start()
    p1.start()
    # p.join() 逐個執行進程 使得不併發

def hello(name):
    print("hello, ",name)

if __name__ == "__main__":
    create_process()

​ 在使用併發設計的時候最好儘可能的避免共享數據,但是在特殊情況下multiprocessing提供了ValueArray兩種方式將數據存儲在一個共享內存地圖中

from multiprocessing import Array,Value

def process_share():
    num = Value('d',1.1) #第一個參數 'd'表示數據類型 爲double
    arr = Array('i',range(11)) #第一個參數 'i'表示數據類型 爲整型
    display_sharedata(num,arr)
    a = Process(target = changedata,args=(num,arr) )
    b = Process(target = changedata,args=(num,arr) )
    #使用多進程的常規方法是,先依次調用start啓動進程,再依次調用join要求主進程等待子進程的結束
    a.start()
    b.start()
    a.join() #join是用來阻塞當前線程的
    display_sharedata(num,arr)
    b.join()
    display_sharedata(num,arr)
    
def changedata(num,arr):
    num.value = 0.0
    for i in range(len(arr)):
        arr[i] += 1         

def display_sharedata(num,arr):
    print("the value : ",num.value)
    print("the array : ",end='')
    for i in arr:
        print(i,end=' ')
    print()


if __name__ == "__main__":
    process_share()

​ 另外multiprocessing模塊也提供了更爲強大的Manager來實現進程間數據共享,由Manager()返回的manager提供list, dict, Namespace, Lock, RLock, Semaphore, BoundedSemaphore, Condition, Event, Barrier, Queue, Value and Array類型的支持

from multiprocessing import Manager

def f(d,l):
    d["name"] = "zhangyanlin"
    d["age"] = 18
    d["Job"] = "pythoner"
    l.reverse()
  
def process_manager():
    with Manager() as man:
        d = man.dict()
        l = man.list(range(10))
  
        p = Process(target=f,args=(d,l))
        p.start()
        p.join()
  
        print(d)
        print(l)

if __name__ == "__main__":
    process_manager()

進程池

​ Pool類描述了一個工作進程池,進程池內部維護一個進程序列,當使用時,則去進程池中獲取一個進程,如果進程池序列中沒有可供使用的進進程,那麼程序就會等待,直到進程池中有可用進程爲止。

​ Pool構造方法的幾個參數

  • processes :使用的工作進程的數量,如果processes是None那麼使用 os.cpu_count()返回的數量。

  • initializer: 如果initializer是None,那麼每一個工作進程在開始的時候會調用initializer(*initargs)。

  • maxtasksperchild:工作進程退出之前可以完成的任務數,完成後用一個心的工作進程來替代原進程,來讓閒置的資源被釋放。maxtasksperchild默認是None,意味着只要Pool存在工作進程就會一直存活。

  • context: 用在制定工作進程啓動時的上下文,一般使用 multiprocessing.Pool() 或者一個context對象的Pool()方法來創建一個池,兩種方法都適當的設置了context

​ Pool類的幾個方法

  • apply(func[, args[, kwds]]) :使用arg和kwds參數調用func函數,結果返回前會一直阻塞,由於這個原因,apply_async()更適合併發執行,另外,func函數僅被pool中的一個進程運行
  • apply_async(func[, args[, kwds[, callback[, error_callback]]]]) : apply()方法的一個變體,會返回一個結果對象。如果callback被指定,那麼callback可以接收一個參數然後被調用,當結果準備好回調時會調用callback,調用失敗時,則用error_callback替換callback。 Callbacks應被立即完成,否則處理結果的線程會被阻塞。
  • close() : 阻止更多的任務提交到pool,待任務完成後,工作進程會退出。
  • terminate() : 不管任務是否完成,立即停止工作進程。在對pool對象進程垃圾回收的時候,會立即調用terminate()。
  • join() : wait工作線程的退出,在調用join()前,必須調用close() or terminate()。這樣是因爲被終止的進程需要被父進程調用wait(join等價與wait),否則進程會成爲殭屍進程。
from multiprocessing import Pool
import time

def func_apply(i):
    time.sleep(1)
    print("I recieved %d"%i)
    return i + 100

def pool_apply():
    pool = Pool(5)
    for i in range(1,20):
        pool.apply(func=func_apply,args=(i,))
    pool.close()
    pool.join()     
        
def func_callback(arg):
    print("the arg is %d"%arg)
    
def pool_apply_async():
    pool = Pool(5)
    for i in range(1,20):
        pool.apply_async(func=func_apply,args=(i,),callback=func_callback)
   	pool.close()
    pool.join()
    
if __name__=="__main__":
    pool_apply()
    # pool_apply_async()

協程

​ 線程和進程的操作是由程序觸發系統接口,最後的執行者是系統;event loop協程的操作則是程序員。協程存在的意義:對於多線程應用,CPU通過切片的方式來切換線程間的執行,線程切換時需要耗時(保存狀態,下次繼續)。協程,則只使用一個線程,在一個線程中規定某個代碼塊執行順序。(協程的適用場景:當程序中存在大量不需要CPU的操作時(IO),適用於協程;)

​ 協程的特性:

  • 註冊、執行、取消延時調用(異步函數)

  • 創建用於通信的client和server協議(工具)

  • 創建和別的程序通信的子進程和協議(工具)

  • 把函數調用送入線程池中

​ 協程的使用:

  • asyncio.get_event_loop() : asyncio啓動默認的event loop
  • run_until_complete() : 這個函數是阻塞執行的,知道所有的異步函數執行完成
  • close() : 關閉event loop

​ 協程的第三方庫:gevent

import asyncio
   
async def cor1():
    print("COR1 start")
    await cor2()
    print("COR1 end")
   
async def cor2():
    print("COR2")
   
loop = asyncio.get_event_loop()
loop.run_until_complete(cor1())
loop.close()

參考文獻

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章