末學者筆記--Python函數三玄

Python函數三玄

《主題:多任務》

【一】:多線程:                                                               

特色:

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

# 無序的,併發的算法

 

1.無序性

import threading
def test(n):
    time.sleep(1)
    print('task',n)
for i in range(10):
    t = threading.Thread(target=test,args=('t_%s' % i,))    #注意,參數args後接的是一個元組,當只有一個參數時,要加一個逗號,不然運行會報錯
    t.start()
編程

》》多線程

task t_0併發

task t_1app

task t_4async

task t_3函數

task t_2性能

task t_5ui

task t_7

task t_9

task t_8

task t_6

》》

 

2.共享全局變量

import threading
num=0
def test1():
    global num     #局部變量只能使用全局變量,不可更改,更改須要加全局聲明:‘global
    num+=100
def test2():
    print(num)
t1 = threading.Thread(target=test1)
t2 = threading.Thread(target=test2)
t1.start()
t2.start()

 

3.計算併發所用的時間

import threading

import time

def test1(n):

    time.sleep(1)

    print('task', n)

def test2(n):

    time.sleep(1)

    print('task', n)

 

start = time.time()

l = []

t1 = threading.Thread(target=test1, args=(1,))

t2 = threading.Thread(target=test1, args=(2,))

t1.start()

t2.start()

l.append(t1)

l.append(t2)

for i in l:

    i.join()

end = time.time()print(end - start)

 

4.GIL全局解釋器鎖

GIL的全稱是:Global Interpreter Lock,意思就是全局解釋器鎖,這個GIL並非python的特性,他是隻在Cpython解釋器裏引入的一個概念,而在其餘的語言編寫的解釋器裏就沒有這個GIL例如:JythonPypy

爲何會有gil?:

    隨着電腦多核cpu的出現核cpu頻率的提高,爲了充分利用多核處理器,進行多線程的編程方式更爲普及,隨之而來的困難是線程之間數據的一致性和狀態同步,而python也利用了多核,因此也逃不開這個困難,爲了解決這個數據不能同步的問題,設計了gil全局解釋器鎖。

說到gil解釋器鎖,咱們容易想到在多線程中共享全局變量的時候會有線程對全局變量進行的資源競爭,會對全局變量的修改產生不是咱們想要的結果,而那個時候咱們用到的是python中線程模塊裏面的互斥鎖,哪樣的話每次對全局變量進行操做的時候,只有一個線程可以拿到這個全局變量;看下面的代碼:

 

import threading
import time
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)

print(global_num)

》》

104420

126353

》》

 

在上面的例子裏,咱們建立了兩個線程來爭奪對global_num的加一操做,可是結果並不是咱們想要的

緣由2個:

 

(1)併發無序性

當執行代碼時,會併發三個線程:2個函數線程,一個main主線程(即運行代碼的線程);由於其線程執行是無序的,就有可能加一操做的函數線程未完成,主線程就已經輸出值,並且函數線程操做過大時發生的機率就更大;

 

解決方法

在最後輸出global_num前加入:

t1.join()
t2.join()

join’其效果就是等待線程執行完畢

 

(2)資源爭搶

當解決(1)問題後,其運行結果大約爲100萬多,並無變成200萬,緣由以下圖:

 

 

 

【解釋】:假設當一次操做時,t1 線程拿到共享值num=20,其申請gil鎖(不允許別人使用此值),接着進入cpu執行 +1 操做,但還未完成加一算法時,執行時間到了,被要求釋放gil鎖,此時t2開始運行(其運行初值num=20),進行相同操做返回num=21;接着運行t1以前未完成的操做(即給20進行+1操做),返回值依然爲num=21;最終致使2次加一操做重複,而當這種操做過多時發生的次數也就越多,致使不能加到200萬。

 

解決方法咱們在這裏加入互斥鎖,使得我一個加值操做完成後,纔可被其餘線程使用共享變量,其鎖住的代碼段是串行的,但未鎖段依然併發。

 

import threading
import time
lock = threading.Lock()    #注意實例化完整
global_num = 0
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

》》

 

【補】:IO密集與CPU密集

1CPU密集型(CPU-bound

CPU密集型也叫計算密集型,指的是系統的硬盤、內存性能相對CPU要好不少,此時,系統運做大部分的情況是CPU Loading 100%CPU要讀/I/O(硬盤/內存)I/O在很短的時間就能夠完成,而CPU還有許多運算要處理,CPU Loading很高。

 

在多重程序系統中,大部份時間用來作計算、邏輯判斷等CPU動做的程序稱之CPU bound。例如一個計算圓周率至小數點一千位如下的程序,在執行的過程中絕大部份時間用在三角函數和開根號的計算,即是屬於CPU bound的程序。

 

CPU bound的程序通常而言CPU佔用率至關高。這多是由於任務自己不太須要訪問I/O設備,也多是由於程序是多線程實現所以屏蔽掉了等待I/O的時間。

 

2IO密集型(I/O bound

IO密集型指的是系統的CPU性能相對硬盤、內存要好不少,此時,系統運做,大部分的情況是CPU在等I/O (硬盤/內存) 的讀/寫操做,此時CPU Loading並不高。

 

I/O bound的程序通常在達到性能極限時,CPU佔用率仍然較低。這多是由於任務自己須要大量I/O操做,而pipeline作得不是很好,沒有充分利用處理器能力。

 

【二】多進程:                                                                     

1. 含義:

一個程序運行起來以後,代碼+用到的資源稱之爲進程,它是操做系統分配資源的基本單位,不只能夠經過線程完成多任務,進程也是能夠的

 

2.特色

#進程之間是相互獨立的
#cpu密集的時候適合用多進程

#多進程比較耗費資源

 

3.多進程併發

import multiprocessing
import time
from multiprocessing import Pool

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

if __name__ == '__main__':     #多進程必需要這一步
    p1 = multiprocessing.Process(target=test1)
    p2 = multiprocessing.Process(target=test2)
    p1.start()
    p2.start()

》》

task1 0

task2 0

task1 1

task2 1

task1 2

task2 2

task1 3

task2 3

task1 4

task2 4

task1 5

task2 5

task1 6

task2 6

task1 7

task2 7

task1 8

task2 8

task1 9

task2 9

》》

 

 

 

 

4.進程之間不共享

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

》》

0

》》

 

5.進程池

import multiprocessing
from multiprocessing import Pool
import time
g_num = 0
def test1(n):
    for i in range(n):
        time.sleep(1)
        print('test1', i)
def test2(n):
    for i in range(n):
        time.sleep(1)
        print('test2', i)
def test3(n):
    for i in range(n):
        time.sleep(1)
        print('test3', i)
def test4(n):
    for i in range(n):
        time.sleep(1)
        print('test4', i)
if __name__ == '__main__':
    pool = Pool(3)      #把進程聲明出來括號裏不寫東西說明無限制,若是寫數字,就是最大的進程數
    pool.apply_async(test1,(10,))     #用pool去調用函數test1,若是函數有參數,參數爲10,格式爲(10,)
    pool.apply_async(test2,(10,)) 

    pool.apply_async(test3,(10,))
    pool.apply_async(test4,(10,))
    pool.close()     #close必須在join的前面
    pool.join()

 

【三】協程併發(gevent)                                                 

1.特色

# 進程是資源分配的單位

# 線程是操做系統調度的單位

# 進程切換須要的資源最大,效率低

# 線程切換須要的資源通常,效率通常

# 協程切換任務資源很小,效率高

# 多進程、多線程根據cpu核數不同多是並行的,可是協在一個線程中

#協程碰見io就切換

 

2.協程,自動切換

import gevent,time

from gevent import monkey

monkey.patch_all()

def test1():

    for i in range(10):

        time.sleep(1)

        print('test1', 1)

def test2():

    for i in range(10):

        time.sleep(2)

        print('test2', 1)

 

g1 = gevent.spawn(test1)

g2 = gevent.spawn(test2)

g1.join()

g2.join()

 

=================================分割線========================================== 

相關文章
相關標籤/搜索