線程,進程
定義:
進程: 是對各種資源管理的集合,qq 要以一個整體的形式暴露給操作系統管理,里面包含對各種資源的調用,內存的管理,網絡接口的調用等
線程: 是操作系統最小的調度單位, 是一串指令的集合。
進程要想操作CPU,就必須要創建一個線程(進程中至少包含一個線程)
區別:
1.線程共享內存空間(共享數據等),進程的內存空間是獨立的
2.同一進程的線程之間可以相互交流 ,2個進程之間的交流必須通過一個中間代理
3.線程可以操作和控制其他線程(同一進程下),進程只能操作和控制子進程。


對主線程的更改可能會影響到其他線程的工作,對父進程的更改(除非關閉)不會影響子進程。(子進程還可以派生子進程)
Python中的多線程
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根據指定算法進行調度,分片執行指令。
更多方法:
- start 線程准備就緒,等待CPU調度
- setName 為線程設置名稱
- getName 獲取線程名稱
- setDaemon 設置為后台線程或前台線程(默認)
如果是后台線程,主線程執行過程中,后台線程也在進行,主線程執行完畢后,后台線程不論成功與否,均停止
如果是前台線程,主線程執行過程中,前台線程也在進行,主線程執行完畢后,等待前台線程也執行完成后,程序停止 - join 逐個執行每個線程,執行完畢后繼續往下執行,該方法使得多線程變得無意義
- run 線程被cpu調度后自動執行線程對象的run方法
互斥鎖(Lock、RLock)
由於線程之間是進行隨機調度,並且每個線程可能只執行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
"""

信號量(Semaphore)
互斥鎖同時只允許一個線程更改數據,而信號量鎖是同時允許一定數量的線程更改數據 ,多個線程同時執行完畢。
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()
事件(event)
python線程的事件用於主線程控制其他線程的執行,事件主要提供了兩個方法:event.set()設定,event.clear()沒設定。
- event.wait():等待設定
- event.is_set():判斷是否設定
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()
Python中的多進程
多進程特點:
- 每一個進程都是由父進程啟動的
- 子進程被父進程啟動后就是獨立的(父進程copy了一份給子進程)
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()
進程池:
進程池內部維護一個進程序列,當使用時,則去進程池中獲取一個進程,如果進程池序列中沒有可供使用的進進程,那么程序就會等待,直到進程池中有可用進程為止。
進程池中有兩個方法:
- apply:串行
- apply_async:並行

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不用鎖)
協程的好處:
- 無需線程上下文切換的開銷
- 無需原子操作鎖定及同步的開銷
- "原子操作(atomic operation)是不需要synchronized",所謂原子操作是指不會被線程調度機制打斷的操作;這種操作一旦開始,就一直運行到結束,中間不會有任何 context switch (切換到另一個線程)。原子操作可以是一個步驟,也可以是多個操作步驟,但是其順序是不可以被打亂,或者切割掉只執行部分。視作整體是原子性的核心。
- 方便切換控制流,簡化編程模型
- 高並發+高擴展性+低成本:一個CPU支持上萬的協程都不是問題。所以很適合用於高並發處理。
缺點:
- 無法利用多核資源:協程的本質是個單線程,它不能同時將 單個CPU 的多個核用上,協程需要和進程配合才能運行在多CPU上.當然我們日常所編寫的絕大部分應用都沒有這個必要,除非是cpu密集型應用。
- 進行阻塞(Blocking)操作(如IO時)會阻塞掉整個程序
使用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 同步與異步
總結:
1、多進程,多線程,協程
操作系統方面的多進程,多線程,協程:
操作系統可以開多個進程,一個進程可以有多個線程,多個線程可以被分配到不同的核心上跑,但實際上每個核心上只有一個線程,只是這個線程在不停的進行上下文的切換,給我們一種並發的感覺。
協程:單線程的調度機制。它的作用是讓原來要使用異步+回調方式(調用線程)寫的非人類代碼,可以用看似同步的方式寫出來。協程是先出現的,但它有明顯的時間差,沒有並發的感覺,所以出現了線程。
python的多進程,多線程,協程:
但python的多線程只能在一個核心上跑(創始人沒想到會有多核出現),就是單核的上下文切換,所以很雞肋。於是協程在python大展拳腳,好多框架都是使用協程來解決多任務的,而不是線程(scrapy,tornado)。
python中多進程,多線程,協程的使用:
IO密集型:多線程/協程(可以用異步),cpu占用率低,單個cpu核心就夠了
CPU密集型:多進程,多給它幾個核心提升性能
2、python多線程不用join,進程需要(否則子進程會在進程結束時強制被關閉)
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, 因為每個子線程的超時開始時刻是上一個子線程超時結束的時刻。
