python入門三(多執行緒,多程序)
python語言提供了對多執行緒、多程序的支援,下面來看看相關知識
一、python多執行緒
1、python多執行緒模組
python的多執行緒提供了兩個模組即thread和threading,threading這個模組提供了更多的功能和更高階的使用方式所以thread我們就不學了,只學threading模組!
2、該模組常用屬性、方法
threading模組的Thread 類,這個類的物件就是一個執行緒,這個類有很多屬性和方法,比如name(執行緒名)、daemo(是否要設定為守護執行緒)
Thread類的相關方法:start()啟動執行緒、join()在該執行緒執行完之前主執行緒的阻塞isAlivel()執行緒是否結束、isDaemon()判斷是否為守護執行緒
3、python多執行緒demo
import threading from time import sleep,ctime loops =[2,4] def loop(nloop,nsec): print ("start nloop %s at %s"%(nloop,ctime())) sleep(nsec) print("loop done at "+str(ctime())) def main(): print ("start time:"+str(ctime())) threads =[] nloops =range(len(loops)) for i in nloops: t = threading.Thread(target=loop,args=(nloops,loops[i],)) threads.append(t) for i in nloops: threads[i].start() for i in nloops: threads[i].join() print("all threads end") if __name__=="__main__": main()
我們使用的是threading模組,所以匯入threading而不是thread 定義一個列表,兩個元素 loop()方法,該方法又兩個引數即一個常量和休眠秒數,用方法引數和ctime()給佔位符賦值 sleep(nsec)休眠時間 main()方法中定義了一個列表threads[] 獲取一個隨機數nloops 遍歷建立執行緒物件並給這個執行緒繫結任務以及需要的引數,當然還可以設定名稱name =如果不設定有預設值 呼叫threading 模組的Thread物件,其中target的值就是給該執行緒繫結的方法(任務),args=()引數傳的是元組 使用列表的append方法將執行緒物件新增到列表中 執行緒物件呼叫start()方法啟動執行緒 執行緒物件呼叫join()方法阻塞主執行緒直到該執行緒結束
4、python執行緒池demo
from concurrent.futures import ThreadPoolExecutor
import time
def return_future_result(message):
time.sleep(2)
return message
pool = ThreadPoolExecutor(max_workers=2)
future1 = pool.submit(return_future_result, ("hello"))
future2 = pool.submit(return_future_result, ("world"))
print(future1.done())
time.sleep(3)
print(future2.done())
print(future1.result())
print(future2.result())
這裡使用了執行緒池來執行任務,如果任務個數大於執行緒池的最大數則任務會等待執行緒否則不用等待!
這裡對多執行緒只是簡單的瞭解,沒有吧所有的實現方式都列出來,如果之前學過java的多執行緒你會發現python的多執行緒中能看到java的所有多執行緒的影子,我只是說類名稱什麼很像!!!
二、python多程序
在說程序前先來看看python中的直譯器全域性鎖(GIL)
Python程式碼的執行由Python 虛擬機器(也叫直譯器主迴圈,CPython版本)來控制,Python 在設計之初就考慮到要在直譯器的主迴圈中,同時只有一個執行緒在執行,即在任意時刻,只有一個執行緒在直譯器中執行。對Python 虛擬機器的訪問由全域性直譯器鎖(GIL)來控制,正是這個鎖能保證同一時刻只有一個執行緒在執行。
全域性直譯器鎖GIL設計理念與限制
GIL的設計簡化了CPython的實現,使得物件模型,包括關鍵的內建型別如字典,都是隱含可以併發訪問的。鎖住全域性直譯器使得比較容易的實現對多執行緒的支援,但也損失了多處理器主機的平行計算能力。
但是,不論標準的,還是第三方的擴充套件模組,都被設計成在進行密集計算任務是,釋放GIL。
還有,就是在做I/O操作時,GIL總是會被釋放。對所有面向I/O 的(會呼叫內建的作業系統C 程式碼的)程式來說,GIL 會在這個I/O 呼叫之前被釋放,以允許其它的執行緒在這個執行緒等待I/O 的時候執行。如果是純計算的程式,沒有 I/O 操作,直譯器會每隔 100 次操作就釋放這把鎖,讓別的執行緒有機會執行(這個次數可以通過 sys.setcheckinterval 來調整)如果某執行緒並未使用很多I/O 操作,它會在自己的時間片內一直佔用處理器(和GIL)。也就是說,I/O 密集型的Python 程式比計算密集型的程式更能充分利用多執行緒環境的好處。
ok這樣大體上了解了python多執行緒,在網上也有說python的多執行緒就是雞肋……我們不管那些,通過上面我們知道,python的多執行緒確實不太適合用在cpu密集型的應用中,其實python就不適合用在cpu密集型的應用中。
但python給我們還提供了多程序,能用多程序的我們就可以使用多進行來執行這樣就能更好的利用計算機資源
1、python程序的模組
python 多程序有三個模組包括subprocess、multiprocessing、concurrent.futures 這裡我們也就簡單學習一下後兩個的使用方式
python程序的建立:Process([group [, target [, name [, args [, kwargs]]]]]),target表示呼叫物件,args表示呼叫物件的位置引數元組。kwargs表示呼叫物件的字典。name為別名。group實質上不使用。
2、該模組的常用屬性、方法
方法:is_alive()、join([timeout])、run()、start()、terminate()。其中,Process以start()啟動某個程序。
屬性:authkey、daemon(要通過start()設定)、exitcode(程序在執行時為None、如果為–N,表示被訊號N結束)、name、pid。其中daemon是父程序終止後自動終止,且自己不能產生新程序,必須在start()之前設定。
3、python程序demo
(1)、單一程序:
import multiprocessing
import time
def worker(interval):
n = 5
while n > 0:
print("The time is {0}".format(time.ctime()))
time.sleep(interval)
n -= 1
if __name__ == "__main__":
p = multiprocessing.Process(target = worker, args = (3,))
p.start()
print (p.pid:"+str(p.pid))
print (p.name:"+str(p.name))
print (p.is_alive:"+str(p.is_alive()))
(2)、多程序:(這個直接copy的)
import multiprocessing
import time
def worker_1(interval):
print ("worker_1")
time.sleep(interval)
print ("end worker_1")
def worker_2(interval):
print ("worker_2")
time.sleep(interval)
print ("end worker_2")
def worker_3(interval):
print ("worker_3")
time.sleep(interval)
print ("end worker_3")
if __name__ == "__main__":
p1 = multiprocessing.Process(target = worker_1, args = (2,))
p2 = multiprocessing.Process(target = worker_2, args = (3,))
p3 = multiprocessing.Process(target = worker_3, args = (4,))
p1.start()
p2.start()
p3.start()
print("The number of CPU is:" + str(multiprocessing.cpu_count()))
for p in multiprocessing.active_children():
print("child p.name:" + p.name + "\tp.id" + str(p.pid))
print ("END!!!!!!!!!!!!!!!!!")
4、python程序池:
在利用Python進行系統管理的時候,特別是同時操作多個檔案目錄,或者遠端控制多臺主機,並行操作可以節約大量的時間。當被操作物件數目不大時,可以直接利用multiprocessing中的Process動態成生多個程序,十幾個還好,但如果是上百個,上千個目標,手動的去限制程序數量卻又太過繁瑣,此時可以發揮程序池的功效。
Pool可以提供指定數量的程序,供使用者呼叫,當有新的請求提交到pool中時,如果池還沒有滿,那麼就會建立一個新的程序用來執行該請求;但如果池中的程序數已經達到規定最大值,那麼該請求就會等待,直到池中有程序結束,才會建立新的程序來它。
(1)、非阻塞程序池:
import multiprocessing
import time
def func(msg):
print ("msg:"+str(msg))
time.sleep(3)
print ("end")
if __name__ == "__main__":
pool = multiprocessing.Pool(processes = 3)
for i in range(4):
msg = "hello %d" %(i)
pool.apply_async(func, (msg, )) #維持執行的程序總數為processes,當一個程序執行完畢後會新增新的程序進去
print ("Mark~ Mark~ Mark~~~~~~~~~~~~~~~~~~~~~~")
pool.close()
pool.join() #呼叫join之前,先呼叫close函式,否則會出錯。執行完close後不會有新的程序加入到pool,join函式等待所有子程序結束
print ("Sub-process(es) done.")
apply_async(func[, args[, kwds[, callback]]]) 它是非阻塞,
apply(func[, args[, kwds]])是阻塞的(理解區別,看例1例2結果區別)
close()關閉pool,使其不在接受新的任務。
terminate() 結束工作程序,不在處理未完成的任務。
join() 主程序阻塞,等待子程序的退出, join方法要在close或terminate之後使用。
(2)、阻塞程序池:
import multiprocessing
import time
def func(msg):
print ("msg:"+str(msg))
time.sleep(3)
print ("end")
if __name__ == "__main__":
pool = multiprocessing.Pool(processes = 3)
for i in range(4):
msg = "hello %d" %(i)
pool.apply(func, (msg, )) #維持執行的程序總數為processes,當一個程序執行完畢後會新增新的程序進去
print ("Mark~ Mark~ Mark~~~~~~~~~~~~~~~~~~~~~~")
pool.close()
pool.join() #呼叫join之前,先呼叫close函式,否則會出錯。執行完close後不會有新的程序加入到pool,join函式等待所有子程序結束
print ("Sub-process(es) done.")
從python3.2開始concurrent.futures被納入了標準庫,所以我們可以使用程序池來實現,具體demo據不列出來了,上班真苦逼,沒時間學……
可以參考這篇文章:http://www.tuicool.com/articles/rq6bMf
總結:
通過上面的簡單學習能簡單實用python多執行緒,多程序了,但通過學習發現,在api的使用上多執行緒和多程序很相似,不管是使用就得執行緒池程序池還是新的你可能也會發現他們的這些使用方式和java的多執行緒很相似(使用上相似,底層實現不知道),所以還是那句話觸類旁通。
注:本人學習python只是為了寫一些簡單的指令碼,所以不會深入學習相關知識點更不會全面深入學習,所以想深入學習的請忽略!