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()
設置flag
爲True
;clear
設置flag
爲False
。
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
提供了Value
和Array
兩種方式將數據存儲在一個共享內存地圖中
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()