python ---線程,進程,協程

本章內容python

  1. 線程
  2. 進程
  3. 協程

線程是最小的調度單位
進程是最小的管理單元編程

線程

多線程的特色:

  1. 線程的併發是利用cpu上下文切換
  2. 多線程的執行的順序是無序的
  3. 多線程共享全局變量
  4. 線程是繼承在進程裏的,沒有進程就沒有線程
  5. GIL全局解釋器鎖
  6. 只有在進行耗時的IO操做的時候,能釋放GIL,因此只要在IO密集型的代碼裏,用多線程就很合適

多線程的好處

  1. 達到充分利用CPU的目的。多線程完成cpu內核的快速切換,提升CPU的利用率
  2. 多線程能夠防止數據阻塞問題,多條線程同時運行,哪怕一條線程的代碼執行讀取數據阻塞,也不會影響其它任務的執行。
  3. 多線程能夠提升系統I/O問題的處理效率等

並行和併發

  • 並行:指在同一時刻,有多條指令在多個處理器上同時執行;
  • 併發:指在同一時刻,只能有一條指令執行,但多個進程指令被快速輪換執行,使得在宏觀上具備多個進程同時執行的效果。
#併發

import time ,threading
def test1(n):
    for i in range(n):
        time.sleep(1)
        print('task1',i)

def test2(n):
    for i in range(n):
        time.sleep(1)
        print('task2',i)

thread1 = threading.Thread(target=test1,args=(10,))
thread2 = threading.Thread(target=test2,args=(10,))
thread1.start()
thread2.start()

.......................................


#運行共用10s
#無序

import time,threading
def test1(n):
    time.sleep(1)
    print('task',n)

for i in range(10):
    thread1 = threading.Thread(target=test1,args=(i,))
    thread1.start()

...............................運行結果
task 1
task 2
task 3
task 7
task 5
task 4
task 6
task 0
task 9
task 8
#共享全局變量

import threading
num=0
def test1():
    global num
    for i in range(10):
        num+=1

def test2():
    global num
    print(num)

thread1 = threading.Thread(target=test1)
thread2 = threading.Thread(target=test2)
thread1.start()
thread2.start()

..............................................運行結果

10
#GIL全局解釋器鎖實例

import threading
global_num = 0

def test1():
    global global_num
    for i in range(1000000):
        global_num += 1

def test2():
    global global_num
    for i in range(1000000):
        global_num += 1
t1 = threading.Thread(target=test1)
t2 = threading.Thread(target=test2)
t1.start()
t2.start()
print(global_num)

......................................運行結果
109704

#運行結果本應該爲2000000,爲何運行結果遠小於應得結果?

第一個緣由:運行代碼後加上咱們啓動的兩個線程,cpu共起了3個線程;其中一個用於執行代碼(取名a),兩個用於執行函數test1和test2(t1 t2);a線程運行到print(global_num)時,test1和test2循環尚未執行完畢!線程a就已經輸出了;因此獲得的值不是2000000多線程

解決方法:讓a線程等test1和test2循環完成以後再執行print();併發

#緣由1解決後

import threading
global_num = 0

def test1():
    global global_num
    for i in range(1000000):
        global_num += 1

def test2():
    global global_num
    for i in range(1000000):
        global_num += 1
t1 = threading.Thread(target=test1)
t2 = threading.Thread(target=test2)
t1.start()
t2.start()
t1.join()
t2.join()
print(global_num)

...........................................運行結果
1240490

#爲何仍是不等於2000000呢?

第二個緣由:GIL全局解釋器鎖app

GIL全局解釋器鎖:Global Interpreter Lock,意思就是全局解釋器鎖,這個GIL並非python的特性,他是隻在Cpython解釋器裏引入的一個概念;隨着電腦多核cpu的出現核cpu頻率的提高,爲了充分利用多核處理器,進行多線程的編程方式更爲普及,隨之而來的困難是線程之間數據的一致性和狀態同步,而python也利用了多核,因此也逃不開這個困難,爲了解決這個數據不能同步的問題,設計了gil全局解釋器鎖。異步

gil全局解釋器鎖的做用:多線程共享全局變量的時候,gil全局解釋器鎖能夠保證同一時間內只有一個線程能夠拿到這個全局變量;async

看圖講解:函數

  1. t1拿到全局變量count=0
  2. t1申請gil鎖(gil全局解釋器鎖能夠保證只有一個線程能夠拿到這個全局變量)
  3. t1調用系統線程
  4. t1到cpu上執行+1操做
  5. t1執行操做時可能偷懶了,沒加完,但時間到了t1只能把gil鎖給了t2
  6. t2在此以前已經拿到count全局變量count=0(注意此時覺得t1沒執行完+1操做,count=0)
  7. t2拿到gil鎖
  8. t2調用系統線程
  9. t2到cpu上執行+1操做
  10. t2勤勤懇懇的+1,在時間內完成了加一操做
  11. t2 完成給count賦值(此時count=1)
  12. t1又拿到gil鎖,繼續未完成的操做,
  13. t1完成操做並賦值給count=1,(此時count仍是=1)
#解決方法:調用threading模塊的lock鎖

import threading
global_num = 0
lock=threading.Lock()   #實例化一個lock鎖
def test1():
    global global_num
    lock.acquire()   #鎖上
    for i in range(1000000):
        global_num += 1
    lock.release()    #解鎖

def test2():
    global global_num
    lock.acquire()  #鎖上
    for i in range(1000000):
        global_num += 1
    lock.release()   #解鎖
t1 = threading.Thread(target=test1)
t2 = threading.Thread(target=test2)
t1.start()
t2.start()
t1.join()
t2.join()
print(global_num)

.....................................運行結果
2000000

進程

  1. 一個程序運行起來以後,代碼+用到的資源稱之爲進程,它是操做系統分配資源的基本單位,不只能夠經過線程完成多任務,進程也是能夠的
  2. 進程之間是相互獨立的
  3. 進程啓動耗費的資源是比較大的,可是效率比較高
  4. cpu密集的時候適合用多進程
#多線程實現多任務

import multiprocessing,time
num = 10
def  test1():
    for i in range(num):
        time.sleep(1)
        print('task1',i)
def test2():
    for i in range(num):
        time.sleep(1)
        print('task2',i)
if __name__ == '__main__':
    p1 = multiprocessing.Process(target=test1)
    p2 = multiprocessing.Process(target=test2)
    p1.start()
    p2.start()
.............................運行結果

#共運行10s
#相互獨立資源不共享

import multiprocessing
num = 0
def test1 ():
    global num
    for i in range(10):
        num+=1
    print(num)
def test2():
    global num
    for i in range(10):
        num+=1
    print(num)
if __name__ == '__main__':
    p1 = multiprocessing.Process(target=test1)
    p2 = multiprocessing.Process(target=test2)
    p1.start()
    p2.start()

...............................運行結果

10
10
#進程池

import time ,multiprocessing
from multiprocessing import Pool

def test1(n):
    for i in range(n):
        time.sleep
        print('task1',i)
def test2(n):
    for i in range(n):
        time.sleep(1)
        print('task2',i)

if __name__ == '__main__':
    pool = Pool(1) #容許最大進程數
    pool.apply_async(test1,(10,))  #參數必須元組
    pool.apply_async(test2,(10,))
    pool.close()   
    pool.join()

協程

協程:也叫微線程,協程是在一個線程裏面的
現有進程---> 線程 ---> 協程ui

  1. 進程是資源分配的單位
  2. 線程是操做系統調度的單位
  3. 協程的調度由所在程序自身控制
  4. 進程切換須要的資源最大,效率低
  5. 線程切換須要的資源通常,效率通常
  6. 協程切換任務資源很小,效率高
  7. 多進程、多線程根據cpu核數不同多是並行的,可是協成在一個線程中

異步IO:遇到io請求就切換
第一步:pip 安裝gevent模塊spa

pip install gevent

協程實例:

#異步io

import gevent

def test1(n):
    for i in range(n):
        gevent.sleep(1)
        print('task1',i)
def test2(n):
    for i in range(n):
        gevent.sleep(1)
        print('task2',i)
g1 = gevent.spawn(test1,10)  #協程任務g1
g2 = gevent.spawn(test2,10)  #任務g2
g1.join()
g2.join()

..........................運行結果

#運行共用時10s

分析異步IO運行

協程徹底靠異步IO完成兩個任務:遇到io請求就切換(sleep算IO請求)

相關文章
相關標籤/搜索