2011-03-24 122 views
5

說我有一個寫入文件的函數。我也有一個函數循環重複讀取文件。我有這兩個函數在不同的線程中運行。 (其實我正在通過MDIO讀/寫寄存器,這就是爲什麼我不能同時執行兩個線程,只有一個或另一個線程,但爲了簡單起見,我們只是說它是一個文件)Python:線程+鎖定會顯着減慢我的應用程序的速度

現在當我孤立地運行寫函數,它執行相當快。但是,當我運行線程並讓它在運行之前獲得鎖定時,它似乎運行速度非常慢。這是因爲第二個線程(讀函數)輪詢獲取鎖嗎?有什麼辦法可以解決這個問題嗎?

我目前只是使用一個簡單的RLock,但是我打開任何可以提高性能的改變。

編輯:作爲一個例子,我將舉一個發生了什麼的基本例子。讀線程基本上始終運行,但偶爾會有一個單獨的線程發出調用來加載。如果我通過從cmd提示符運行加載來進行基準測試,則在線程中運行速度至少要慢3倍。

寫線程:

import usbmpC# functions I made which access dll functions for hardware, etc 

def load(self, lock): 
    lock.acquire() 
    f = open('file.txt','r') 
    data = f.readlines() 
    for x in data: 
     usbmpc.write(x) 
    lock.release() 

讀線程:

import usbmpc 

def read(self, lock): 
    addr = START_ADDR 
    while True: 
     lock.acquire() 
     data = usbmpc.read(addr) 
     lock.release() 
     addr += 4 
     if addr > BUF_SIZE: addr = START_ADDR 
+0

在CPython中,除了釋放GIL的C(外部)模塊外,一次只能有一個「運行」的線程,因爲一次只允許一個線程訪問Python引擎。如果MDIO調用沒有釋放GIL,那麼在MDIO調用完成之前,其他函數/ lock *甚至不能啓動*(也就是說,Python代碼不會運行)。 (我不是線程感知的MDIO。) – 2011-03-24 22:56:58

+0

我用一個例子編輯了這篇文章。你是說,一旦寫線程獲得鎖,讀線程永遠不會執行?我在印象之下lock.acquire將輪詢,直到它獲得鎖定?還有什麼可以放慢上述代碼? – 2011-03-24 23:36:26

+0

@Shaunak Amin我沒有試圖說/暗示:-)但CPython線程不能實際運行多個Python線程(它們在內部鎖定在GIL上;只有一些指令允許Python線程「收益「) - 但這並不適用於上述更新,因爲這些鎖是關於usbmpc訪問的。 – 2011-03-25 00:52:28

回答

4

你使用線程多核機器上?

如果答案是肯定的,那麼除非您的Python版本是3.2+,否則在運行線程應用程序時將會降低性能。

David Beazly已經付出了相當大的努力來發現GIL在多內核上發生了什麼,並且讓我們其他人也很容易理解它。檢查他的網站和那裏的資源。您也可以在PyCon 2010上看到他的presentation。它非常有趣。

簡而言之,在Python 3.2中,Antoine Pitrou wrote a new GIL在單核和多核機器上具有相同的性能。在以前的版本中,更多的核心/線程你擁有的性能損耗增加...

希望它能幫助:)

+0

這兩個很好的答案,但這些鏈接給了我很好的繼續。我之前已經決定要麼刪除線程並且自己處理調度(以避免上下文切換傷害性能),因爲我的需求相當原始。我希望我可以勾選兩個答案。 – 2011-03-26 19:32:54

+2

我自己編寫了我自己的調度器來防止GIL在線程之間的上下文切換,而現在花費10分鐘以上的函數現在需要20秒(因爲刪除了幾個線程競爭週期)。這是警告:請勿在PYTHON 2.x上使用Threadeding! – 2011-03-28 18:50:01

+0

@ShaunakAmin:該死的。 – 2012-01-11 11:42:41

4

你爲什麼不收購作家鎖爲每個時間只寫?您目前正在鎖定加載函數的整個持續時間,直到加載函數完成後,讀者纔會進入。

其次,你應該使用上下文鎖。您當前的代碼不是線程安全的:

def load(lock): 
    for x in data: 
     with lock: 
      whatever.write(x) 

對於您的讀者也是如此。使用上下文來保持鎖定。第三,請不要使用RLock。你知道你不需要一個,你的讀寫代碼不需要重新獲取,所以不要給它那個機會,你會掩蓋錯誤。

真正的答案出現在你的問題的幾條評論中:GIL引發了一些爭議(假設它實際上並不是你的誤操作)。 Python threading模塊非常棒,GIL有時不是,但更重要的是它產生的複雜行爲被誤解了。值得一提的是,主流認爲拋出問題並不是人們相信它的靈丹妙藥。它通常不是解決方案。

相關問題