基礎: 什麼是進程(process)? 每一個程式的記憶體是獨立的,例如:world不能訪問QQ。 進程:QQ是以一個整體的形式暴露給操作系統管理,裡面包含了各種資源的調用(記憶體管理、網路介面調用等)。啟動一個QQ,也就是啟動了一個進程。 什麼是線程(thread)? 線程是操作系統能夠進行運算調度的 ...
基礎:
什麼是進程(process)?
每一個程式的記憶體是獨立的,例如:world不能訪問QQ。
進程:QQ是以一個整體的形式暴露給操作系統管理,裡面包含了各種資源的調用(記憶體管理、網路介面調用等)。啟動一個QQ,也就是啟動了一個進程。
什麼是線程(thread)?
線程是操作系統能夠進行運算調度的最小單位。線程包含在進程之中,是進程中的實際運作單位。
一個進程中最少有一個線程。
一個線程時指 進程中一個單一順序的控制流。
一個進程中科院併發多個線程,每條線程並行執行不同的任務,線程與線程之間是相互獨立的。
線程和進程的區別:
進程:對各種資源管理的集合
線程:操作系統最小的調度單位,是一串指令的集合
關係:
進程中第一個線程時主線程,主線程創建其他線程,其他線程也可以創建線程,線程之間是平等的;
進程有父進程、子進程,獨立的記憶體空間,唯一的進程標識符,pid;
什麼是上下文切換?
上下文切換,也稱做進程切換或者任務切換,是指cpu從一個進程或線程切換到另一個進程或線程。舉例說明,如下:
a.開啟QQ和微信,先聊QQ,然後切換到微信進行聊天,再切換到QQ,這個操作就叫做上下文切換。
b.同時開啟多個應用,電腦cpu配置是4核,多個應用之間進行切換時,沒有卡頓現象 也完全感受不到cpu在進行任務切換,因為cpu處理很快,所以應用之間切換沒有卡頓現象;
單線程:
import time import requests def get_res(): urls = [ 'http://www.baidu.com', 'https://www.taobao.com/', 'https://www.jd.com/', 'http://www.meilishuo.com/' ] start = time.time() for url in urls: print(url) resp = requests.get(url) print(resp) end = time.time() print('單線程運行時間:', end - start)
執行結果:
http://www.baidu.com <Response [200]> https://www.taobao.com/ <Response [200]> https://www.jd.com/ <Response [200]> http://www.meilishuo.com/ <Response [200]> 單線程運行時間: 1.0470597743988037
解釋:
a. cpu順序被請求
b.除非cpu從一個url獲取的響應,否則不會去請求下一個url
c. 網路請求會花費較長的時間,所以cpu在等待網路請求的返回時間內一直處於閑置狀態
多線程:
import time import threading def run(count): #每次執行該方法,需要休息2s time.sleep(2) print(count) #開始創建多線程 start = time.time() for i in range(5): #創建線程,指定運行哪個函數,也就是指定哪個函數運行需要創建多線程 #target=要運行的函數名 # args=函數運行傳入的參數,run方法需要傳入count,把創建 th = threading.Thread(target=run, args=(i, )) #啟動線程 th.start() #多線程創建完畢且運行結束 end = time.time() print('運行時間:', end - start)
運行結果:
運行時間: 0.0
1
0
4
2
3
解釋:
a. 列印出來的運行時間統計的不是多線程的運行時間,因為沒有運行run都要等待2s,所以多線程的運行時間至少為2s,那麼列印的結果是什麼?
列印的運行時間是 主線程的運行時間,因為在運行python文件時,如果不啟動多線程,至少有一個線程在運行
線程與線程之間是相互獨立的,最開始運行的是主線程,當運行到threading.Thread時,創建一個線程,創建的線程執行迴圈方,主線程執行其他操作
主線程不等待其他線程結束後再結束
b. 列印出的count數據是無序的,因為多線程運行run方法,並不是第一個請求結束後才進行下一個請求的,而是創建一個線程後執行run方法,接著創建另一個線程,哪個線程執行完畢就會列印出結果
c. 總共創建了5個線程
若想統計多線程總共的執行時間,也就是從開始創建線程 到 線程結束運行之間的時間(不需要考慮線程之間怎麼運行的),操作如下:
join()等待 (等待線程結束)
import time import threading def run(count): #每次執行該方法,需要休息2s time.sleep(2) print(count) #開始創建多線程 start = time.time() #存放創建的所有線程 threads_list = [] for i in range(5): #創建線程,指定運行哪個函數,也就是指定哪個函數運行需要創建多線程 #target=要運行的函數名 # args=函數運行傳入的參數,run方法需要傳入count,把創建 th = threading.Thread(target=run, args=(i, )) #啟動線程 th.start() #把啟動的每一個線程添加到線程組內 threads_list.append(th) for t in threads_list: #主線程迴圈等待每個子線程運行完畢, t代表每個子線程 t.join() #等待線程結束 #多線程創建完畢且運行結束 end = time.time() print('運行時間:', end - start)
執行結果:
0 1 2 4 3 運行時間: 2.0011146068573
守護線程
守護線程:主線程運行結束後,不管守護線程執行是否結束,都會結束,舉例說明:
比如皇帝有很多僕人,當皇帝死了之後,那麼多僕人就得陪葬。
只要非守護線程結束了,不管守護線程結束沒結束,程式都結束
import threading import time def run(count): time.sleep(2) print(count) for i in range(5): #迴圈創建線程,總共5個線程 t = threading.Thread(target=run, args=(i, )) #設置守護線程,新創建的這些線程都是 主線程的 守護線程, 主線程創建一個線程後 就運行結束了 t.setDaemon(True) #啟動線程,守護線程設置必須在start前面 t.start() print('over')
GIL 全局解釋器鎖
例如 4核機器上
Python創建4線程,四個線程均勻分到多核上,但是同時只能一核在處理數據。
python調用操作系統、C語音的原生介面,在出口做了設置。全局解釋器鎖,保證數據統一
所以有人說python的線程是假線程。
在修改數據的時候,為了防止數據改亂了,所以多線程就變成串列處理,但是以為是python在處理,實際上是調用了操作系統的C語音的線程介面,所以中間的過程,python控制不了了,只知道結果。在這種情況下,設置的方式是出口控制,雖然四個線程,但是同一時間只有一個線程在工作。
所以這算是python的一個缺陷,但是也不能說是python的缺陷,是Cpython的缺陷。因為Cpython是C語音寫的,以後python的未來是PYPY。
線程鎖
線程鎖,又叫互斥鎖
線程之間溝通:保證同一時間只有一個線程修改數據
python2.x 中需要加鎖,Python3.x中加不加鎖都一樣,因為解釋器做了優化
import threading from threading import Lock #創建lock對象 num = 0 lock = Lock() #申請一把鎖,創建鎖的對象 def run2(): global num lock.acquire() #修改數據前 加鎖 num += 1 lock.release() #修改後釋放解鎖 lis = [] for i in range(5): #創建線程 t = threading.Thread(target=run2) #啟動線程 t.start() #將啟動的線程添加到線程組內 lis.append(t) for t in lis: #等待線程運行結束 t.join() #num的值為5,執行多次後,會出現不一樣的值 print('over', num)
RLock 遞歸鎖
大鎖中還有小鎖、遞歸鎖,解鎖時就混了,所以用遞歸鎖,Rlock()
import threading,time def run1(): print("grab the first part data") lock.acquire() global num num +=1 lock.release() return num def run2(): print("grab the second part data") lock.acquire() global num2 num2+=1 lock.release() return num2 def run3(): lock.acquire() res = run1() print('--------between run1 and run2-----') res2 = run2() lock.release() print(res,res2) if __name__ == '__main__': num,num2 = 0,0 lock = threading.RLock() # 聲明遞歸鎖 # lock = threading.Lock() # 用互斥鎖,會鎖死了,弄混鎖情況,可以試一下 for i in range(10): t = threading.Thread(target=run3) t.start() while threading.active_count() != 1: print(threading.active_count()) else: print('----all threads done---') print(num,num2)
多線程的另一種寫法:
import threading import time class MyThread(threading.Thread): def __init__(self, num): threading.Thread.__init__(self) self.num = num def run(self): # 定義每個線程要運行的函數 print("running on number:%s" % self.num) time.sleep(3) if __name__ == '__main__': t1 = MyThread(1) t2 = MyThread(2) t1.start() t2.start()
多進程(瞭解即可):
python裡面的多線程,是不能利用多核cpu的,如果想利用多核cpu的話,就得使用多進程
多進程適用CPU密集型任務
多線程適用io密集型任務
from multiprocessing import Process def f(name): time.sleep(2) print('hello', name) if __name__ == '__main__': for i in range(10): p = Process(target=f, args=('niu',)) p.start()
轉載請務必保留此出處:http://www.cnblogs.com/lhly/p/7163791.html