多執行緒適用於阻塞式IO場景,不適用於平行計算場景

2021-10-25 16:06:08 字數 3647 閱讀 5539

python的標準實現是cpython。

cpython執行python**分為2個步驟:首先,將文字原始碼解釋編譯為位元組碼,然後再用乙個直譯器去

解釋執行位元組碼。位元組碼直譯器是有狀態的,需要維護該狀態的一致性,因此使用了gil(global

interpreter lock,全域性直譯器鎖)。

gil的存在,使得cpython在執行多執行緒**的時候,同一時刻只有乙個執行緒在執行,無法利用多cpu

提高運算效率。但是這個特點也帶來了乙個好處:cpython執行多執行緒的時候,內部物件預設就是執行緒

安全的。這個特性,被非常多的python庫開發者所依賴,直到cpython的開發者想要去除gil的時候,

發現已經有大量的**庫重度依賴這個gil帶來的內部物件預設就是執行緒安全的特性,變成乙個無法解

決的問題了。

雖然多執行緒在平行計算場景下無法帶來好處,但是在阻塞式io場景下,卻仍然可以起到提高效率的作

用。這是因為阻塞式io場景下,執行緒在執行io操作時並不需要占用cpu時間,此時阻塞io的執行緒可以被

掛起的同時繼續執行io操作,而讓出cpu時間給其他執行緒執行非io操作。這樣一來,多執行緒並行io操作

就可以起到提高執行效率的作用了。

綜上,python的標準實現cpython,由於gil的存在,同乙個時刻只能執行乙個執行緒,無法充分利用多

cpu提公升運算效率,因此python的多執行緒適用於阻塞式io的場景,不適用於平行計算的場景。

下面舉乙個對計算量有要求的求乙個數的因數分解的**例項,來說明python多執行緒不適用於並行計

算的場景:

# -*- coding:utf-8 -*-

from time import time

from threading import thread

deffactorize

(number)

:for i in

range(1

, number +1)

:if number % i ==0:

yield i

class

factorizethread

(thread)

:def

__init__

(self, number)

: thread.__init__(self)

self.number = number

defrun(self)

: self.factors =

list

(factorize(self.number)

)def

test

(numbers)

: start = time(

)for number in numbers:

list

(factorize(number)

) end = time(

)print

('took %.3f seconds'

%(end - start)

)def

test_thread

(numbers)

: start = time(

) threads =

for number in numbers:

thread = factorizethread(number)

thread.start(

)for t in threads:

t.join(

) end = time(

)print

('mutilthread took %.3f seconds'

%(end - start)

)if __name__ ==

"__main__"

: numbers =

[2139079

,1214759

,1516637

,1852285

] test(numbers)

test_thread(numbers)

**輸出:

took 0.319 seconds

mutilthread took 0.539 seconds

以上**執行結果只是乙個參考值,具體資料跟執行環境相關。但是可以看到單執行緒方式比多執行緒方式

的計算速度要快。由於cpython執行多執行緒**時因為gil的原因導致每個時刻只有乙個執行緒在執行,

因此多執行緒平行計算並不能帶來時間上的收益,反而因為排程執行緒而導致總時間花費更長。

對於io阻塞式場景,多執行緒的作用在於發生io阻塞操作時可以排程其他執行緒執行非io操作,因此在這個

場景下,多執行緒是可以節省時間的。可以用以下的**來驗證:

# -*- coding:utf-8 -*-

from time import time

from threading import thread

import os

defslow_systemcall

(n):

for x in

range

(100):

open

("test_%s"

% n,

"a")

.write(os.urandom(10)

*100000

)def

test_io

(n):

start = time(

)for _ in

range

(n):

slow_systemcall(_)

end = time(

)print

('took %.3f seconds'

%(end - start)

)def

test_io_thread

(n):

start = time(

) threads =

for _ in

range

(n):

thread = thread(target=slow_systemcall, args=

("t_%s"

%_,)

) thread.start(

)for thread in threads:

thread.join(

) end = time(

)print

('multithread took %.3f seconds'

%(end - start)

)if __name__ ==

"__main__"

: n =

5 test_io(n)

test_io_thread(n)

**輸出:

took 5.179 seconds

multithread took 1.451 seconds

可以看到單執行緒花費時間與多執行緒花費時間之比接近1:4,考慮執行緒排程的時間,這個跟一般語言的多線

程起的作用比較相似。這是因為當python執行io操作時,實際上是執行了系統呼叫,此時執行緒會釋放

gil,直到系統呼叫結束時,再申請獲取gil,也就是在io操作期間,執行緒確實是並行執行的。

python的另外乙個實現jpython就沒有gil,但是它並不是最常見的python實現。

rewrite場景例項(適用於開發)

需求 使用者訪問course 11 22 33.html實際上真實訪問是 course 11 22 33 course 33.html root web01 conf.d cat url.oldxu.com.conf server rewrite中的flag 跳轉 redirect 302 臨時跳轉...

QZXing不適用於C 11語法?

重新整理寫過的程式,發現了乙個詭異的問題。將使用了qzxing的源程式包移動到另乙個資料夾後,build,qzxing中的某個檔案出現錯誤。一步步回溯問題可能出現的地方。發現這個程式最開始的版本中並沒有需要c 11支援的語法 如lambda表示式 因此.pro檔案中也沒有config c 11這一句...

extjs和jquery各適用於什麼場景

比較點 extjs jquery 是否收費 extjs3版本開始對於商業用途是實行收費的 開源免費 庫檔案大小 500kb 20kb 是否有及css要載入是否 支援ui 豐富的ui 要使用外掛程式 是否支援物件導向程式設計 支援 復用性強 不支援粒度 extjs是基於元件級 jquery主要是簡化d...