實驗發現不涉及IO輸入的多線程,串行運行在老的(新解釋器縮短差距)解釋器有時候比多線程快,這是什麼緣由?python
GIL:全局解釋鎖(這玩意跟python語言無關,跟解釋種類有關,只對CPython解釋器有用,可是這種站主導市場)
由於有GIL,因此同一時刻,只有一個線程被一個CPU執行
多核對於Python多線程用不上,完了!!!
多進程能夠用上多核,可是進程的開銷大!!安全
總結:多線程用不上多核(計算密集型任務時,不如串行;IO密集型任務它仍是不錯的),多進程太多時開銷大,因此用多進程+協程。數據結構
1 #!/usr/bin/env python 2 #-*-coding:utf-8 -*- 3 4 ''' 5 python 大都編輯器的單一進程中的多線程是沒法用多核的,由於GIL;可是多進程能夠,因此多進程模塊multiprocessing 6 線程的調用兩種方法,一是直接用treading模塊,二是繼承這個模塊。進程類似(守護進程deamon有點區別) 7 若是有4個CPU,開4個進程,就能夠實現真的並行,而不是併發 8 ''' 9 10 import time, multiprocessing 11 import threading 12 13 def f(name): 14 time.sleep(1) 15 print("hello",name,time.ctime()) 16 17 if __name__ == '__main__': 18 plist = [] 19 for i in range(2): 20 p = multiprocessing.Process(target=f,args=("wan",)) 21 # p = threading.Thread(target=f,args=('wan',)) 22 plist.append(p) 23 p.start() 24 25 for i in plist: 26 i.join() 27 28 print("ending........")
1 from multiprocessing import Process 2 import os 3 import time 4 5 6 def info(title): 7 print("title:", title) 8 print('parent process:', os.getppid()) # 每一個進程有一個ID號,getppid是獲得父進程id 9 print('process id:', os.getpid()) # getpid是獲得本身當前運行的id 10 11 def f(name): 12 13 info('function f') 14 print('hello', name) 15 16 17 if __name__ == '__main__': 18 19 info('main process line') 20 21 time.sleep(1) 22 print("------------------") 23 p = Process(target=info, args=('yuan',)) # 這裏的父進程的ID號應該是等於上面info調用的後等到的process id 同樣 24 p.start() 25 p.join()
介紹一下multiprocessing.Process類:多線程
is_alive() 返回進程是否運行併發
terminate () 無論任務是否完成,當即中止工做進程app
進程之間的通信,由於一個進程中的多個線程是在同一塊進程的內存空間中,他們數據共享,很簡單。可是進程之間的內存空間不一樣,要解決進程之間的通信,以及共享數據問題(我改了數據,你拿到的應該是我改後的)。進程中涉及大量的數據拷貝,因此資源消耗大。異步
說隊列前,先介紹一下列表這種數據結構,看看它在線程中的缺陷,線程不安全。async
1 import threading,time 2 3 li = [1, 2, 3, 4, 5] 4 5 def pri(): # 每一個線程刪除列表中的最後一個元素 6 while li: 7 a = li[-1] 8 print(a) 9 time.sleep(1) 10 li.remove(a) 11 12 t1 = threading.Thread(target=pri) 13 t2 = threading.Thread(target=pri) 14 15 t1.start() 16 t2.start() 17 18 t1.join() 19 t2.join() 20 print("end.............")
用了隊列,剛纔列表在線程中的問題就不存在了。編輯器
1 import queue 2 3 q = queue.Queue(3) # 先進先出,後進先出是.LifeoQueue,還能夠按級別出.PriorityQueue 4 5 q.put(12) # put 與 get還有一些其餘的參數 6 q.put("hello") 7 q.put({'name':"wan"}) 8 9 print(q.qsize()) # 隊列中實際有多少值 10 print(q.empty()) # 是否爲空 11 print(q.full()) # 是否爲滿 12 13 while 1: 14 data = q.get() 15 print(data) 16 print("--------------")
一樣,用隊列進行進程中的通信以下:ide
1 import multiprocessing 2 # import queue 3 4 5 def foo(q): 6 7 q.put(123) 8 q.put("wan") 9 10 if __name__ == '__main__': 11 q = multiprocessing.Queue() # 進程中有本身的隊列 12 plist = [] 13 for i in range(3): 14 p = multiprocessing.Process(target=foo,args=(q,)) 15 # 這裏必須把參數q傳進去,由於它不像線程那樣共享,在主線程創建q,只是主進程內存空間中,子進程中隊列都沒定義 16 plist.append(p) 17 p.start() 18 print(q.get()) 19 print(q.get()) 20 print(q.get()) 21 print(q.get())
1 from multiprocessing import Process, Pipe 2 def f(conn): 3 conn.send([12, {"name":"yuan"}, 'hello']) # 子進程發 4 response=conn.recv() # 子進程收 5 print("response",response) 6 conn.close() 7 # print("q_ID2:",id(conn)) 8 9 if __name__ == '__main__': 10 11 parent_conn, child_conn = Pipe() # 雙向管道,相似電話的兩頭,一頭給子進程,一頭給主進程 12 13 # print("q_ID1:",id(child_conn)) 14 p = Process(target=f, args=(child_conn,)) 15 p.start() 16 17 print(parent_conn.recv()) # 父進程(主進程)接收到的 18 parent_conn.send("兒子你好!") # 父進程發 19 p.join()
1 from multiprocessing import Process, Manager 2 3 def f(d, l,n): 4 5 d[n] = '1' #{0:"1"} 6 d['2'] = 2 #{0:"1","2":2} 7 8 l.append(n) #[0,1,2,3,4, 0,1,2,3,4,5,6,7,8,9] 9 #print(l) 10 11 12 if __name__ == '__main__': 13 14 with Manager() as manager: # 相似用with打開文件,省略關的功能 15 16 d = manager.dict() #{} 17 18 l = manager.list(range(5)) #[0,1,2,3,4] 19 20 21 p_list = [] 22 23 for i in range(10): 24 p = Process(target=f, args=(d,l,i)) 25 p.start() 26 p_list.append(p) 27 28 for res in p_list: 29 res.join() 30 31 print(d) 32 print(l)
#!/usr/bin/env python #-*-coding:utf-8 -*- ''' 線程有同步(同步鎖):由於它們共享數據,可能會出錯; 進程數據不共享,可是也共享一些資源(好比屏幕(進程1與進程2可能同一時刻想同時輸出在屏幕上,也就是出現串行錯誤)) 所以,進程也須要同步multiprocessing.Lock ''' from multiprocessing import Process,Lock def f(l,i): l.acquire() print('hello world %s'%i) l.release() if __name__ == '__main__': lock = Lock() for num in range(12): Process(target=f,args=(lock, num)).start() print('ending.............')
1 ''' 2 進程池:控制開進程的最大量 multiprocessing.Pool 3 進程池兩個方法:apply同步 , apply_async異步 4 5 回調函數:就是某個動做或者某個函數執行成功後再去執行的函數 6 感受回調函數能夠把內容寫在它跟的函數後面,這樣基本就等同了 7 可是callback是在主進程下面調用的 8 ''' 9 10 from multiprocessing import Process,Pool 11 import time,os 12 13 def f(i): 14 time.sleep(3) 15 print(i) 16 print("son pid: ",os.getpid()) 17 return 'hello %s'%i 18 19 def Bar(arg): # 回調函數必須至少有一個參數,就是它跟的函數的返回值 20 print(arg) 21 print('callback pid: ',os.getpid()) 22 23 if __name__ == '__main__': 24 pool = Pool(5) 25 print("main pid: ",os.getpid()) 26 for i in range(100): 27 pool.apply_async(func=f,args=(i,),callback=Bar) # callback是回調函數名字,它在每一個子進程執行完後執行 28 29 pool.close() 30 pool.join() # 注意這個pool.close與pool.join的順序是固定的