python---進程與線程,python---進程線程
進程和線程什麼是線程(thread)什麼是進程
線程:作業系統能夠進行運算調度的最小單位。它被包含在進程中,是進程中的實際運作單位。是一串指令的集合
一個線程指的是進程中一個單一順序的控制流程,一個進程是中可以並發多個線程,每個線程並存執行不同的任務
進程:以一個整體的形式暴露給作業系統管理,裡麵包含對各種資源的調用,記憶體的管理,網路介面的調用等,對各種資源管理的集合,就可以稱為進程
進程要操作cpu,必須先建立一個線程,所有在同一個進程裡的線程是共用同一塊記憶體空間的
注意:1.進程本身不能夠執行
2.進程和線程不能比較誰快誰慢,兩個沒有可比性,進程是資源的集合,線程是真正執行任務的,進程要執行任務也要通過線程
3.啟動一個線程比啟動一個進程快
進程和線程的區別
1.線程共用記憶體空間,進程的記憶體是獨立的
2.thread have direct access to data segment of its process;process have their own copy of the data segment of the parent process
3.同一個進程之間線程可以互相交流,兩個進程想要通訊,必須通過一個中間代理來實現
4.建立一個線程很簡單,建立一個新進程需要對其父進程做一次複製
5.一個線程可以控制和操作同一個進程裡的其他的線程,但是進程只能操作子進程
6.改變一個主線程可能會影響到其他線程的運行,對父進程的修改不會影響到子進程
Python GIL(全域解譯器鎖)
在python中無論你啟用多少個線程,你有多少個cpu,在python執行的時候都會在同一時刻只准許一個線程運行
原因:python的線程是叫用作業系統的原生線程
線程鎖:
1 import threading 2 import time 3 def run(n): 4 lock.acquire() 5 global num 6 num += 1 7 #time.sleep(1)在上鎖的情況不要使用sleep,不然會等50s才會完成 8 lock.release() 9 lock = threading.Lock()10 num = 011 start_time = time.time()12 t_objs = []13 for i in range(50):14 t = threading.Thread(target = run,args = ("t-%s" %i,))15 t.start()16 t_objs.append(t)17 for t in t_objs:18 t.join()19 print("-----all threads has finished....",threading.current_thread(),threading.active_count())20 21 print("num = ",num)
給線程上鎖,使程式變成串列,保證num在+1的時候能夠準確無誤,不然可能會導致,一個線程正在執行+1還沒有結束,另外一個線程也開始+1,最後達不到準確結果
遞迴鎖:在一個大鎖中包含一個小鎖
情境:學校放學,學生離校了,突然發現自己的文具盒落在了教師中,學校每次只准許一個人進學校(其他東西丟了就是這個人乾的),進教室拿的時候也要開教室門(防止有其他人在學校沒有走),最後拿到文具盒,離開學校。這個期間學校大門相當於一把鎖,教室的門相當於另一把鎖。保證了整個過程沒有其他人的幹擾
1 import threading,time 2 3 def run1(): 4 print("grab the first part data") 5 lock.acquire() 6 global num 7 num += 1 8 lock.release() 9 return num10 11 def run2():12 print("grab the second part data")13 lock.acquire()14 global num215 num2 += 116 lock.release()17 return num218 19 def run3():20 lock.acquire()21 res = run1()22 print('------between run1 and run2')23 res2 = run2()24 lock.release()25 print(res,res2)26 27 num,num2 = 0,028 lock = threading.RLock()29 for i in range(10):30 t = threading.Thread(target=run3)31 t.start()32 33 while threading.active_count() != 1:34 print(threading.active_count())35 else:36 print("----all threads done---")37 print(num,num2)
相當於先進入run3這個大門,然後在進入run1和run2這個兩個小門,然後進行num1和num2的加1,保證了每個線程的執行都是串列的
Python threading模組
線程有2中調用方式
直接調用:
1 import threading 2 import time 3 4 def run(n): 5 print("task ",n) 6 time.sleep(2) 7 8 t1 = threading.Thread(target = run,args = ("t1",)) 9 t2 = threading.Thread(target = run,args = ("t2",))10 11 t1.start()12 t2.start()
運行結果:一下子出來兩個結果,但是程式還會等待2s才會結束,一共兩秒,因為他們是並行的
繼承式調用:
1 import threading 2 import time 3 4 #用類的方式啟動線程 5 class MyThread(threading.Thread): 6 def __init__(self,n): 7 super(MyThread,self).__init__() 8 self.n = n 9 def run(self):10 print("running task ",self.n)11 12 t1 = MyThread("t1")13 t2 = MyThread("t2")14 t1.start()15 t2.start()
上面只啟動了2個線程,我們下面啟動50個線程:
1 import threading 2 import time 3 4 def run(n): 5 print("task ",n) 6 time.sleep(2) 7 8 start_time = time.time() 9 for i in range(50):10 11 t = threading.Thread(target = run,args = ("t-%s" %i,))12 t.start()13 print("cost : ",time.time()-start_time)
運行結果:
問題:建立了50個進程之後只用了這點時間,主程式沒有等其他的子線程就往下走
原因:是多線程的原因,一個程式至少有一個線程,程式本身就是一個線程,主線程,主線程啟動了子線程之後,子線程就和主線程沒有關係了,兩個互不影響
解決方案:join()方法,等子線程運行完成之後,主程式才往下走
import threadingimport timedef run(n): print("task ",n) time.sleep(2) print("task has done.... ",n)start_time = time.time()t_objs = []for i in range(50): t = threading.Thread(target = run,args = ("t-%s" %i,)) t.start() t_objs.append(t)for t in t_objs: t.join()print("------all threads has finished")print("cost : ",time.time()-start_time)
先自己建立一個臨時列表,然後儲存建立的線程,然後一個一個的用join()方法
建立了50個線程,主線程就是程式的本身,也就是說上面一共有51個線程
#threading.current_thread()顯示當前線程是主線程還是子線程#threading.active_count()顯示當前程式啟動並執行線程的個數print("-----all threads has finished....",threading.current_thread(),threading.active_count()) 守護進程
t.setDaemon(True) #把當前線程設定成守護線程,要在start()之前
非守護線程結束,所有守護線程就退出
1 import threading 2 import time 3 def run(n): 4 print("task ",n) 5 time.sleep(2) 6 print("task has done.... ",n) 7 start_time = time.time() 8 t_objs = [] 9 for i in range(50):10 11 t = threading.Thread(target = run,args = ("t-%s" %i,))12 t.setDaemon(True)#把當前線程設定成守護線程,要在start()之前13 t.start()14 t_objs.append(t)15 16 print("-----all threads has finished....",threading.current_thread(),threading.active_count())17 print("cost : ",time.time()-start_time)
運行結果:task has done都沒有運行,主線程結束之後,所有子線程都強制結束
Semaphore(訊號量)
互斥鎖同時只允許一個線程更改資料,而Semaphore是同時允許一定數量的線程更改資料,如有3個座位,最多允許3個人同時在座位上,後面的人只能等到有人起來才能夠坐進去
1 import threading,time 2 def run(n): 3 semaphore.acquire() 4 time.sleep(1) 5 print("run the thread:%s\n" %n) 6 semaphore.release() 7 8 if __name__ == '__main__': 9 10 semaphore = threading.BoundedSemaphore(5)#最多允許5個線程同時運行11 for i in range(20):12 t = threading.Thread(target = run,args=(i,))13 t.start()14 15 16 while threading.active_count() != 1:17 pass#print(threading.active_count())18 else:19 print("----all threads done---")20 21 #一次性執行了5個線程
和線程鎖差不多,都要acquire()和release(),區別就是訊號量有多把鎖,
threading.BoundedSemaphore(5)最多允許5個線程同時運行,那運行結果就是5個結果一起出來,但是實質是這5個要是有3個先完成就會立刻再送進去3個,它不會等5個都完成,它是每出來一個就放進去一個
這5個如果同時改資料就有可能改錯,這個主要用於:連結池 ,線程池。mysql連結有連結池的概念,同一時刻最多有幾個並發;socketserver,為了保證系統不會被太多線程拉慢,可以用訊號量弄同一時刻最多有100個連結進來
事件(event)
a events is a simple synchronization object;
the event represents an internal flag,and threads can wait for the flag to be set,or set or clear the flag themselves
事件是一個簡單的同步對象,事件就相當於設定一個全部變數,然後線程不斷的檢測這個變數的變化然後進行不同的操作
方法:
event = threading.Event()產生一個event對象
event.set()
event.clear()
event.wait()
if the flag is set,the wait method doesn't do anything
標誌位設定,代表綠燈,直接通行
if the flag is cleared,wait will block until it becomes set again
標誌位被清空,代表紅燈,wait等待變綠燈
any number of threads my wait for the same event
紅綠燈案例:
1 import threading 2 import time 3 4 event = threading.Event() 5 def lighter(): 6 count = 0 7 event.set()#一開始先設成綠燈 8 while True: 9 if count > 5 and count <=10 :#改成紅燈10 event.clear()#清空標誌位11 print("\033[41;1mred light is on ...\033[0m")12 elif count > 10:13 event.set()#變綠燈14 print("\033[42;1mgreen light is on ...\033[0m")15 count = 016 else:17 print("\033[42;1mgreen light is on ...\033[0m")18 time.sleep(1)19 count += 120 def car(name):21 while True:22 if event.is_set():#代表綠燈23 print("[%s] running..." %name)24 time.sleep(1)25 else:26 print("[%s] sees red light,waiting..." %name)27 event.wait()28 print("\033[34;1m[%s] green light is on,start going ...\033[0m" %name)29 30 light = threading.Thread(target = lighter,)31 light.start()32 car1 = threading.Thread(target = car,args=("Tesla",))33 car1.start()
運行結果:
queue隊列
queue is especially useful in threaded programming when information must be exchanged safely between multiple threads
class queue.Queue(maxsize = 0)先入先出
class queue.LifeQuene(maxsize = 0)last in first out
class queue.PriorityQueue(maxsize = 0)儲存資料時可設定優先權的隊列
隊列就是一個有順序的容器
列表和隊列的區別:列表取出資料後,資料還在列表中,相當如複製資料;隊列的資料只有一份,取走就沒有了
class queue.Queue:
利用put(),get()方法往隊列裡加減資料,用qsize()方法得到此刻隊列中還有多少資料。
但是有一個問題:當資料都取出來之後,如果還用get()方法去取資料的話,程式就會卡主
可以用get_nowait()方法,如果queue中沒有了資料,就會拋出一個異常,這樣就不會被卡住,同時也可以用qsize()方法進行判斷,如果沒有了資料就不要取了
Queue.get(block = True,timeout=None) block參數,如果取不到資料,預設就會卡住,改成false就不會卡住;timeout設定卡住幾秒
class queue.LifoQueue(maxsize = 0)#last in first out
1 import queue2 q = queue.LifoQueue()3 q.put(1)4 q.put(2)5 q.put(3)6 print(q.get())7 print(q.get())8 print(q.get())
運行結果:
class queue.PriorityQueue(maxsize = 0)#儲存資料室可以設定優先權隊列
1 import queue 2 q = queue.PriorityQueue() 3 4 q.put((10,"d1")) 5 q.put((-1,"d2")) 6 q.put((3,"d4")) 7 q.put((6,"d5")) 8 print(q.get()) 9 print(q.get())10 print(q.get())11 print(q.get())
運行結果:
生產者消費者模型:
在並發編程中使用生產者和消費者模式能夠解決大多數並發問題,該模式通過平衡生產線程和消費線程的工作能力來提高程式的整體處理資料的速度
例子:
1 import threading,time 2 import queue 3 q = queue.Queue(maxsize = 10) 4 def Producer(name): 5 count = 1 6 while True: 7 q.put("骨頭:%s" %count) 8 print("生產了骨頭:",count) 9 count += 110 time.sleep(0.5)11 12 13 14 def Consumer(name):15 while True:16 if q.qsize() > 0:17 print("[%s] 取到 [%s],並且吃到了它。。。" %(name,q.get()))18 time.sleep(1)19 p = threading.Thread(target = Producer,args=("xiaoming",))20 c = threading.Thread(target = Consumer,args=("xiaohong",))21 c1 = threading.Thread(target = Consumer,args=("liangliang",))22 p.start()23 c.start()24 c1.start()
運行結果: