pythonmultitask
⑴ python非同步編程4:協程函數,協程對象,await關鍵字
協程函數:async def 函數名。3.5+
協程對象:執行協程函數()得到的協程對象。
3.5之後的寫法:
3.7之後的寫法:更簡便
await後面 跟 可等待的對象。(協程對象,Future,Task對象 約等於IO等待)
await實例2:串列執行。 一個協程函數裡面可以支持多個await ,雖然會串列,但是如果有其他協程函數,任務列表也在執行,依然會切換。只是案例中的main對應執行的others1和others2串列 。 await會等待對象的值得到之後才繼續往下走。
⑵ python 多線程和多進程的區別 mutiprocessing theading
在socketserver服務端代碼中有這么一句:
server = socketserver.ThreadingTCPServer((ip,port), MyServer)
ThreadingTCPServer這個類是一個支持多線程和TCP協議的socketserver,它的繼承關系是這樣的:
class ThreadingTCPServer(ThreadingMixIn, TCPServer): pass
右邊的TCPServer實際上是主要的功能父類,而左邊的ThreadingMixIn則是實現了多線程的類,ThreadingTCPServer自己本身則沒有任何代碼。
MixIn在Python的類命名中很常見,稱作「混入」,戲稱「亂入」,通常為了某種重要功能被子類繼承。
我們看看一下ThreadingMixIn的源代碼:
class ThreadingMixIn:
daemon_threads = False
def process_request_thread(self, request, client_address):
try:
self.finish_request(request, client_address)
self.shutdown_request(request)
except:
self.handle_error(request, client_address)
self.shutdown_request(request)
def process_request(self, request, client_address):
t = threading.Thread(target = self.process_request_thread,
args = (request, client_address))
t.daemon = self.daemon_threads
t.start()
在ThreadingMixIn類中,其實就定義了一個屬性,兩個方法。其中的process_request()方法實際調用的正是Python內置的多線程模塊threading。這個模塊是Python中所有多線程的基礎,socketserver本質上也是利用了這個模塊。
socketserver通過threading模塊,實現了多線程任務處理能力,可以同時為多個客戶提供服務。
那麼,什麼是線程,什麼是進程?
進程是程序(軟體,應用)的一個執行實例,每個運行中的程序,可以同時創建多個進程,但至少要有一個。每個進程都提供執行程序所需的所有資源,都有一個虛擬的地址空間、可執行的代碼、操作系統的介面、安全的上下文(記錄啟動該進程的用戶和許可權等等)、唯一的進程ID、環境變數、優先順序類、最小和最大的工作空間(內存空間)。進程可以包含線程,並且每個進程必須有至少一個線程。每個進程啟動時都會最先產生一個線程,即主線程,然後主線程會再創建其他的子線程。
線程,有時被稱為輕量級進程(Lightweight Process,LWP),是程序執行流的最小單元。一個標準的線程由線程ID,當前指令指針(PC),寄存器集合和堆棧組成。另外,線程是進程中的一個實體,是被系統獨立調度和分派的基本單位,線程自己不獨立擁有系統資源,但它可與同屬一個進程的其它線程共享該進程所擁有的全部資源。每一個應用程序都至少有一個進程和一個線程。在單個程序中同時運行多個線程完成不同的被劃分成一塊一塊的工作,稱為多線程。
舉個例子,某公司要生產一種產品,於是在生產基地建設了很多廠房,每個廠房內又有多條流水生產線。所有廠房配合將整個產品生產出來,單個廠房內的流水線負責生產所屬廠房的產品部件,每個廠房都擁有自己的材料庫,廠房內的生產線共享這些材料。公司要實現生產必須擁有至少一個廠房一條生產線。換成計算機的概念,那麼這家公司就是應用程序,廠房就是應用程序的進程,生產線就是某個進程的一個線程。
線程的特點:
線程是一個execution context(執行上下文),即一個cpu執行時所需要的一串指令。假設你正在讀一本書,沒有讀完,你想休息一下,但是你想在回來時繼續先前的進度。有一個方法就是記下頁數、行數與字數這三個數值,這些數值就是execution context。如果你的室友在你休息的時候,使用相同的方法讀這本書。你和她只需要這三個數字記下來就可以在交替的時間共同閱讀這本書了。
線程的工作方式與此類似。CPU會給你一個在同一時間能夠做多個運算的幻覺,實際上它在每個運算上只花了極少的時間,本質上CPU同一時刻只能幹一件事,所謂的多線程和並發處理只是假象。CPU能這樣做是因為它有每個任務的execution context,就像你能夠和你朋友共享同一本書一樣。
進程與線程區別:
同一個進程中的線程共享同一內存空間,但進程之間的內存空間是獨立的。
同一個進程中的所有線程的數據是共享的,但進程之間的數據是獨立的。
對主線程的修改可能會影響其他線程的行為,但是父進程的修改(除了刪除以外)不會影響其他子進程。
線程是一個上下文的執行指令,而進程則是與運算相關的一簇資源。
同一個進程的線程之間可以直接通信,但是進程之間的交流需要藉助中間代理來實現。
創建新的線程很容易,但是創建新的進程需要對父進程做一次復制。
一個線程可以操作同一進程的其他線程,但是進程只能操作其子進程。
線程啟動速度快,進程啟動速度慢(但是兩者運行速度沒有可比性)。
由於現代cpu已經進入多核時代,並且主頻也相對以往大幅提升,多線程和多進程編程已經成為主流。Python全面支持多線程和多進程編程,同時還支持協程。
⑶ Python怎麼多線程中添加協程
由於python是一種解釋性腳本語言,python的多線程在運行過程中始終存在全局線程鎖。
簡單的來說就是在實際的運行過程中,python只能利用一個線程,因此python的多線程並不達到C語言多線程的性能。
可以使用多進程來代替多線程,但需要注意的是多進程最好不要涉及到例如文件操作的頻繁操作IO的功能。
⑷ 如何用Python寫一個每分每時每天的定時程序
1.計算生日是星期幾
當你女朋友要過生日了,你肯定要定找家飯店訂個餐慶祝一下,餐館工作日會空一些,周末位置不好定,要是能知道她的生日是星期幾就好了,下面這個程序就能搞定~~
比如girl friend 的生日假設是 gf_birthday='2017-3-3'
1).我們先把變數格式化成一個datetime對象
birthday=datetime.datetime.strptime(gf_birthday,'%Y-%m-%d')
2).然後利用datetime裡面的函數weekday來得到一個下標
birthday.weekday()
3).構造一個weekdays的列表,根據下標從列表裡面取出是周幾
weekdays=['Monday','Tuesday','Wednesday','Thursday','Friday','Saturday','Sunday']
weekdays[birthday.weekday()]
當然你要計算比如情人節,聖誕節什麼的都可以用上面的程序,或者整個列表把10年的節日都羅列計算一下都是可以了,是不是很簡單,對日期的理解有木有加深了一下下
2.定時任務
在Python裡面,比如你想定期去爬一個網頁,或者做運維的同學想每天12點去定時download一個文件,或者定時去掃描一些伺服器,甚至老闆的需求不停的變可能是,每隔5分鍾,或者每小時的整點10分,每周每月都有一些定時任務
用Python怎麼破很簡單,下面這個程序輕松搞定
我們先從一個最簡單的例子說,假設我們是每分種的第10秒,去執行一個任務去列印一下當前的目錄
1).window下是dir命令,linux是ls
我們用platform這個模塊來判斷一下操作系統
import platform
os_platfrom=platform.platform()
if os_platfrom.startswith('Darwin'):
print'this is mac os system'
os.system('ls')
elif os_platfrom.startswith('Window'):
print'this is win system'
os.system('dir')
2).如何定時執行
a.我們先獲取當前的時間
now=datetime.datetime.now()
假設當前時間是2017-02-09 20:19:47.555000
b.然後我們輸入一個你要定時執行的target時間
比如你是x分10秒的時候執行sched_Timer=datetime.datetime(x,x,x,x,x,10)
前面的x是並不重要(只要最後是10秒就行了),我們就把目標時間設的比當前晚一點即可:
sched_Timer=datetime.datetime(2017,2,9,20,20,10)
c.好當時間到了20:20:10的時候要運行我們的程序
如何定時到了呢,很簡單用
if now==sched_Timer:
'run Task'
d.那麼如何讓時間在下一分鍾10秒繼續執行呢,也很簡單用timedelta()
datetime.timedelta(minutes=1)把target時間往後增加一分鍾
sched_Timer=sched_Timer+datetime.timedelta(minutes=1)
然後外邊用個while 死循環hold住就可以了
同樣的這個代碼也可以擴展,把minutes=1改成hours=1就變成了每個小時定時任務,改成days=1就變成每天的定時任務
⑸ Python實現簡單多線程任務隊列
Python實現簡單多線程任務隊列
最近我在用梯度下降演算法繪制神經網路的數據時,遇到了一些演算法性能的問題。梯度下降演算法的代碼如下(偽代碼):
defgradient_descent(): # the gradient descent code plotly.write(X, Y)
一般來說,當網路請求 plot.ly 繪圖時會阻塞等待返回,於是也會影響到其他的梯度下降函數的執行速度。
一種解決辦法是每調用一次 plotly.write 函數就開啟一個新的線程,但是這種方法感覺不是很好。 我不想用一個像 cerely(一種分布式任務隊列)一樣大而全的任務隊列框架,因為框架對於我的這點需求來說太重了,並且我的繪圖也並不需要 redis 來持久化數據。
那用什麼辦法解決呢?我在 python 中寫了一個很小的任務隊列,它可以在一個單獨的線程中調用 plotly.write函數。下面是程序代碼。
classTaskQueue(Queue.Queue):
首先我們繼承 Queue.Queue 類。從 Queue.Queue 類可以繼承 get 和 put 方法,以及隊列的行為。
def__init__(self, num_workers=1): Queue.Queue.__init__(self) self.num_workers=num_workers self.start_workers()
初始化的時候,我們可以不用考慮工作線程的數量。
defadd_task(self, task,*args,**kwargs): args=argsor() kwargs=kwargsor{} self.put((task, args, kwargs))
我們把 task, args, kwargs 以元組的形式存儲在隊列中。*args 可以傳遞數量不等的參數,**kwargs 可以傳遞命名參數。
defstart_workers(self): foriinrange(self.num_workers): t=Thread(target=self.worker) t.daemon=True t.start()
我們為每個 worker 創建一個線程,然後在後台刪除。
下面是 worker 函數的代碼:
defworker(self): whileTrue: tupl=self.get() item, args, kwargs=self.get() item(*args,**kwargs) self.task_done()
worker 函數獲取隊列頂端的任務,並根據輸入參數運行,除此之外,沒有其他的功能。下面是隊列的代碼:
我們可以通過下面的代碼測試:
defblokkah(*args,**kwargs): time.sleep(5) print「Blokkah mofo!」 q=TaskQueue(num_workers=5) foriteminrange(1): q.add_task(blokkah) q.join()# wait for all the tasks to finish. print「Alldone!」
Blokkah 是我們要做的任務名稱。隊列已經緩存在內存中,並且沒有執行很多任務。下面的步驟是把主隊列當做單獨的進程來運行,這樣主程序退出以及執行資料庫持久化時,隊列任務不會停止運行。但是這個例子很好地展示了如何從一個很簡單的小任務寫成像工作隊列這樣復雜的程序。
defgradient_descent(): # the gradient descent code queue.add_task(plotly.write, x=X, y=Y)
修改之後,我的梯度下降演算法工作效率似乎更高了。如果你很感興趣的話,可以參考下面的代碼。 classTaskQueue(Queue.Queue): def__init__(self, num_workers=1):Queue.Queue.__init__(self)self.num_workers=num_workersself.start_workers() defadd_task(self, task,*args,**kwargs):args=argsor()kwargs=kwargsor{}self.put((task, args, kwargs)) defstart_workers(self):foriinrange(self.num_workers):t=Thread(target=self.worker)t.daemon=Truet.start() defworker(self):whileTrue:tupl=self.get()item, args, kwargs=self.get()item(*args,**kwargs)self.task_done() deftests():defblokkah(*args,**kwargs):time.sleep(5)print"Blokkah mofo!" q=TaskQueue(num_workers=5) foriteminrange(10):q.add_task(blokkah) q.join()# block until all tasks are doneprint"All done!" if__name__=="__main__":tests()
⑹ python之多線程
進程的概念:以一個整體的形式暴露給操作系統管理,裡麵包含各種資源的調用。 對各種資源管理的集合就可以稱為進程。
線程的概念:是操作系統能夠進行運算調度的最小單位。本質上就是一串指令的集合。
進程和線程的區別:
1、線程共享內存空間,進程有獨立的內存空間。
2、線程啟動速度快,進程啟動速度慢。注意:二者的運行速度是無法比較的。
3、線程是執行的指令集,進程是資源的集合
4、兩個子進程之間數據不共享,完全獨立。同一個進程下的線程共享同一份數據。
5、創建新的線程很簡單,創建新的進程需要對他的父進程進行一次克隆。
6、一個線程可以操作(控制)同一進程里的其他線程,但是進程只能操作子進程
7、同一個進程的線程可以直接交流,兩個進程想要通信,必須通過一個中間代理來實現。
8、對於線程的修改,可能會影響到其他線程的行為。但是對於父進程的修改不會影響到子進程。
第一個程序,使用循環來創建線程,但是這個程序中一共有51個線程,我們創建了50個線程,但是還有一個程序本身的線程,是主線程。這51個線程是並行的。注意:這個程序中是主線程啟動了子線程。
相比上個程序,這個程序多了一步計算時間,但是我們觀察結果會發現,程序顯示的執行時間只有0.007秒,這是因為最後一個print函數它存在於主線程,而整個程序主線程和所有子線程是並行的,那麼可想而知,在子線程還沒有執行完畢的時候print函數就已經執行了,總的來說,這個時間只是執行了一個線程也就是主線程所用的時間。
接下來這個程序,吸取了上面這個程序的缺點,創建了一個列表,把所有的線程實例都存進去,然後使用一個for循環依次對線程實例調用join方法,這樣就可以使得主線程等待所創建的所有子線程執行完畢才能往下走。 注意實驗結果:和兩個線程的結果都是兩秒多一點
注意觀察實驗結果,並沒有執行列印task has done,並且程序執行時間極其短。
這是因為在主線程啟動子線程前把子線程設置為守護線程。
只要主線程執行完畢,不管子線程是否執行完畢,就結束。但是會等待非守護線程執行完畢
主線程退出,守護線程全部強制退出。皇帝死了,僕人也跟著殉葬
應用的場景 : socket-server
注意:gil只是為了減低程序開發復雜度。但是在2.幾的版本上,需要加用戶態的鎖(gil的缺陷)而在3點幾的版本上,加鎖不加鎖都一樣。
下面這個程序是一個典型的生產者消費者模型。
生產者消費者模型是經典的在開發架構中使用的模型
運維中的集群就是生產者消費者模型,生活中很多都是
那麼,多線程的使用場景是什麼?
python中的多線程實質上是對上下文的不斷切換,可以說是假的多線程。而我們知道,io操作不佔用cpu,計算佔用cpu,那麼python的多線程適合io操作密集的任務,比如socket-server,那麼cpu密集型的任務,python怎麼處理?python可以折中的利用計算機的多核:啟動八個進程,每個進程有一個線程。這樣就可以利用多進程解決多核問題。