定義:python
進程: 是對各類資源管理的集合,qq 要以一個總體的形式暴露給操做系統管理,裏面包含對各類資源的調用,內存的管理,網絡接口的調用等算法
線程: 是操做系統最小的調度單位, 是一串指令的集合。編程
進程要想操做CPU,就必需要建立一個線程(進程中至少包含一個線程)網絡
區別:多線程
1.線程共享內存空間(共享數據等),進程的內存空間是獨立的併發
2.同一進程的線程之間能夠相互交流 ,2個進程之間的交流必須經過一箇中間代理app
3.線程能夠操做和控制其餘線程(同一進程下),進程只能操做和控制子進程。框架
對主線程的更改可能會影響到其餘線程的工做,對父進程的更改(除非關閉)不會影響子進程。(子進程還能夠派生子進程)異步
import threading def run(n): print('運行線程',n) for i in range(10): # 建立10個線程 t = threading.Thread(target=run, args=(i,)) # 線程運行的函數和參數 t.setDaemon(True) # 設置爲守護線程(在主線程線程結束後自動退出,默認爲False即主線程線程結束後子線程仍在執行) t.start() # 啓動線程
上述代碼建立了10個「前臺」線程,而後控制器就交給了CPU,CPU根據指定算法進行調度,分片執行指令。scrapy
更多方法:
因爲線程之間是進行隨機調度,而且每一個線程可能只執行n條執行以後,當多個線程同時修改同一條數據時可能會出現髒數據,因此,出現了線程鎖 - 同一時刻容許一個線程執行操做。
import threading import time gl_num = 0 lock = threading.RLock() # 定義線程鎖 def Func(): lock.acquire() # 開始鎖 global gl_num gl_num += 1 time.sleep(1) print(gl_num) lock.release() # 結束鎖 for i in range(10): t = threading.Thread(target=Func) t.start() """ 沒有鎖的時候打印: >> 10,10,10,10,10,10,10,10,10,10 有鎖的時候打印: >> 1,2,3,4,5,6,7,8,9,10 """
互斥鎖同時只容許一個線程更改數據,而信號量鎖是同時容許必定數量的線程更改數據 ,多個線程同時執行完畢。
import threading, time def run(n): semaphore.acquire() # 信號量鎖開始 time.sleep(1) print("當前運行線程爲: %s" % n) semaphore.release() # 結束 if __name__ == '__main__': num = 0 semaphore = threading.BoundedSemaphore(5) # 最多容許5個線程同時運行(5個5個一塊兒出來) for i in range(20): t = threading.Thread(target=run, args=(i,)) t.start()
python線程的事件用於主線程控制其餘線程的執行,事件主要提供了兩個方法:event.set()設定,event.clear()沒設定。
1 import time,threading 2 3 event = threading.Event() 4 5 def lighter(): 6 count=0 7 event.set() #設定 8 while True: 9 if count<10 and count>=5: 10 event.clear() #清除設定 11 print("\033[41;1m紅燈\033[0m") 12 time.sleep(1) 13 elif count>10: 14 count=0 15 event.set() 16 else: 17 print("\033[42;1m綠燈\033[0m") 18 time.sleep(1) 19 count+=1 20 21 def car(name): 22 while True: 23 if event.is_set(): #判斷是否設定 24 print("\033[32;1m[%s] run...\033[0m"%name) 25 time.sleep(1) 26 else: 27 print('\033[31;1m [%s] stop...'%name) 28 event.wait() #等待設定 29 print('\033[33;1m [%s]走咯'%name) 30 31 32 light=threading.Thread(target=lighter,) 33 34 light.start() 35 36 car1=threading.Thread(target=car,args=('Tesla',)) 37 38 car1.start()
多進程特色:
from multiprocessing import Process def foo(i): print('say hi', i) if __name__ == '__main__': for i in range(10): p = Process(target=foo, args=(i,)) p.start()
獲取進程id:
1 from multiprocessing import Process #多進程 2 import os 3 4 def info(title): 5 print(title) 6 print('module name:', __name__) 7 print('parent process:', os.getppid()) #獲取父進程的端口號 8 print('process id:', os.getpid()) #獲取當前進程的端口號 9 print('\n') 10 11 def f(name): 12 info('\033[31;1mcalled from child process function f\033[0m') 13 print('hello', name) 14 15 if __name__ == '__main__': 16 info('\033[32;1mmain process line\033[0m') 17 p = Process(target=f, args=('bob',)) 18 p.start() 19 p.join() 20 21 get進程id
經過隊列共享數據:
from multiprocessing import Process, Queue def run(qq): qq.put("123") if __name__=='__main__': q=Queue() #生成一個隊列,經過隊列進行傳遞數據 p=Process(target=run,args=(q,)) p.start() print(q.get()) p.join()
經過字典共享數據:
from multiprocessing import Process, Manager import os def f(d, l): d[os.getpid()] =os.getpid() l.append(os.getpid()) print(l) if __name__ == '__main__': with Manager() as manager: #Manager()=manager d = manager.dict() #{} #生成一個字典,可在多個進程間共享和傳遞 l = manager.list(range(5))#生成一個列表,可在多個進程間共享和傳遞 p_list = [] for i in range(10): p = Process(target=f, args=(d, l)) p.start() p_list.append(p) for res in p_list: #等待結果 res.join() print(d) print(l)
在多個進程共享一個屏幕時可能會致使輸出的數據變亂。
from multiprocessing import Process, Lock def f(l, i): l.acquire() #設置進程鎖 try: print('hello world', i) finally: l.release() #取消進程鎖 if __name__ == '__main__': l = Lock() #實例化鎖 for num in range(10): Process(target=f, args=(l, num)).start()
進程池內部維護一個進程序列,當使用時,則去進程池中獲取一個進程,若是進程池序列中沒有可供使用的進進程,那麼程序就會等待,直到進程池中有可用進程爲止。
進程池中有兩個方法:
1 from multiprocessing import Pool 2 import time 3 import os 4 5 def Foo(i): 6 time.sleep(2) 7 print("in process",os.getpid()) 8 return i + 100 9 10 def Bar(arg): 11 print('-->exec done:', arg,os.getpid()) 12 13 if __name__ == '__main__': 14 #freeze_support() 15 pool = Pool(processes=3) #容許進程池同時放入5個進程 16 print("主進程",os.getpid()) 17 for i in range(10): 18 pool.apply_async(func=Foo, args=(i,), callback=Bar) #callback=回調 19 #pool.apply(func=Foo, args=(i,)) #串行 20 #pool.apply_async(func=Foo, args=(i,)) #並行 21 print('end') 22 pool.close() 23 pool.join() #進程池中進程執行完畢後再關閉,若是註釋,那麼程序直接關閉。.join()
經過單線程實現併發(協程只有一個線程,so不用鎖)
協程的好處:
缺點:
使用yield實現協程操做例子
1 import time 2 3 def consumer(name): 4 print("--->starting eating baozi...") 5 while True: 6 new_baozi = yield 7 print("[%s] is eating baozi %s" % (name, new_baozi)) 8 # time.sleep(1) 9 10 def producer(): 11 r = con.__next__() 12 r = con2.__next__() 13 n = 0 14 while n < 5: 15 n += 1 16 con.send(n) 17 con2.send(n) 18 time.sleep(1) 19 print("\033[32;1m[producer]\033[0m is making baozi %s" % n) 20 21 if __name__ == '__main__': 22 con = consumer("c1") 23 con2 = consumer("c2") 24 p = producer() 25 26 yield
同步與異步性能差異
1 import gevent 2 3 def task(pid): 4 gevent.sleep(0.5) 5 print('Task %s done' % pid) 6 7 def synchronous(): #每一個都要等0.5s,須要5s 8 for i in range(1, 10): 9 task(i) 10 11 def asynchronous(): #一共等0.5s 12 threads = [gevent.spawn(task, i) for i in range(10)] 13 gevent.joinall(threads) 14 15 print('Synchronous:') 16 synchronous() 17 18 print('Asynchronous:') 19 asynchronous() 20 21 同步與異步
操做系統方面的多進程,多線程,協程:
操做系統能夠開多個進程,一個進程能夠有多個線程,多個線程能夠被分配到不一樣的核心上跑,但實際上每一個核心上只有一個線程,只是這個線程在不停的進行上下文的切換,給咱們一種併發的感受。
協程:單線程的調度機制。它的做用是讓原來要使用異步+回調方式(調用線程)寫的非人類代碼,能夠用看似同步的方式寫出來。協程是先出現的,但它有明顯的時間差,沒有併發的感受,因此出現了線程。
python的多進程,多線程,協程:
但python的多線程只能在一個核心上跑(創始人沒想到會有多核出現),就是單核的上下文切換,因此很雞肋。因而協程在python大展拳腳,好多框架都是使用協程來解決多任務的,而不是線程(scrapy,tornado)。
python中多進程,多線程,協程的使用:
IO密集型:多線程/協程(能夠用異步),cpu佔用率低,單個cpu核心就夠了
CPU密集型:多進程,多給它幾個核心提高性能
1 python 默認參數建立線程後,無論主線程是否執行完畢,都會等待子線程執行完畢才一塊兒退出,有無join結果同樣
2 若是建立線程,而且設置了daemon爲true,即thread.setDaemon(True), 則主線程執行完畢後自動退出,不會等待子線程的執行結果。並且隨着主線程退出,子線程也消亡。
3 join方法的做用是阻塞,等待子線程結束,join方法有一個參數是timeout,即若是主線程等待timeout,子線程尚未結束,則主線程強制結束子線程。
4 若是線程daemon屬性爲False, 則join裏的timeout參數無效。主線程會一直等待子線程結束。
5 若是線程daemon屬性爲True, 則join裏的timeout參數是有效的, 主線程會等待timeout時間後,結束子線程。此處有一個坑,即若是同時有N個子線程join(timeout),那麼實際上主線程會等待的超時時間最長爲 N * timeout, 由於每一個子線程的超時開始時刻是上一個子線程超時結束的時刻。