2013-03-14 25 views
2

當多個線程訪問同一個函數時,我們是否需要明確或不需要實現機制。Python:通過多線程同時訪問一個沒有鎖定的函數mechansim

我有一個使用線程的程序。有兩個線程,t1t2t1add1()t2爲螺紋的subtract1()。無論同時訪問同一個功能myfunction(caller,num)

1.我已經定義在給定的程序中使用的變量functionLock一個簡單的鎖定機構。這是可靠的還是我們需要修改它?

import time, threading 

functionLock = '' # blank means lock is open   

def myfunction(caller,num): 
    global functionLock 
    while functionLock!='': # check and wait until the lock is open 
     print "locked by "+ str(functionLock) 
     time.sleep(1) 

    functionLock = caller # apply lock 

    total=0 
    if caller=='add1': 
     total+=num 
     print"1. addition finish with Total:"+str(total) 
     time.sleep(2) 
     total+=num 
     print"2. addition finish with Total:"+str(total) 
     time.sleep(2) 
     total+=num 
     print"3. addition finish with Total:"+str(total) 

    else: 
     time.sleep(1) 
     total-=num 
     print"\nSubtraction finish with Total:"+str(total) 

    print '\n For '+caller+'() Total: '+str(total) 

    functionLock='' # release the lock 


def add1(arg1, arg2): 

    print '\n START add' 
    myfunction('add1',10) 
    print '\n END add'   


def subtract1(): 

    print '\n START Sub' 
    myfunction('sub1',100) 
    print '\n END Sub' 


def main(): 

    t1 = threading.Thread(target=add1, args=('arg1','arg2')) 
    t2 = threading.Thread(target=subtract1) 
    t1.start() 
    t2.start() 


if __name__ == "__main__": 
    main() 

輸出如下:

START add 
START Sub 
1. addition finish with Total:10 
locked by add1 
locked by add1 
2. addition finish with Total:20 
locked by add1 
locked by add1 
3. addition finish with Total:30 
locked by add1 
For add1() Total: 30 
END add 
Subtraction finish with Total:-100 
For sub1() Total: -100 
END Sub 

二是確定它不使用鎖?

即使我沒有使用上述程序中定義的鎖定機制,結果對於線程t1和t2來說都是一樣的。這是否意味着python在多個線程訪問同一個函數時自動實現鎖定。

的程序的輸出,而無需使用該鎖,functionLock,在上述程序

START add 
START Sub 
1. addition finish with Total:10 
Subtraction finish with Total:-100 
For sub1() Total: -100 
END Sub 
2. addition finish with Total:20 
3. addition finish with Total:30 
For add1() Total: 30 
END add 

謝謝!

回答

1

除了對這個線程關於繁忙等待一個變量的其他意見,我想指出的是您沒有使用任何類型的原子交換的可能會導致併發錯誤的事實。即使你的測試執行不會導致他們上來,如果執行有不同時序足夠的重複,下面的事件序列可能出現:

線程#1執行while functionLock!=''並得到False。然後,線程#1被中斷(被搶先執行),並且線程#2執行同一行,while functionLock!=''也得到False。在這個例子中,兩個線程都進入了臨界區,這顯然不是你想要的。特別是,在線程修改的任何行中,結果可能不是您所期望的,因爲這兩個線程可以同時位於該部分中。請參閱以下示例:

total10。爲了簡單起見,假設num始終爲1.線程#1執行total+=num,其由三個操作組成:(i)加載值total,(ii)將其加入num和(iii)將結果存儲在total中。如果在(i)之後,線程#1被搶佔並且線程2然後執行total-=num,total被設置爲9。然後,線程#1恢復。但是,它已經加載了total = 10,所以它加1並將11存儲到total變量中。這有效地轉換了線程#2在非操作中的遞減操作。

請注意,在由@ ron-klein鏈接的維基百科文章中,代碼使用xchg操作,該操作將寄存器與變量進行原子交換。這對鎖定的修正至關重要。總而言之,如果您想避免難以置信地調試併發錯誤,切勿將自己的鎖實現爲原子操作的替代方案。

我剛剛注意到,實際上total是您的代碼中的局部變量,所以這絕不會發生。然而,我相信你並不知道這是你完美工作的代碼的原因,因爲你肯定了「這是否意味着當多線程訪問同一個函數時python自動實現鎖定」,這是不正確的。請嘗試將global total添加到myfunction的開頭,並多次執行這些線程,並且您應該在輸出中看到錯誤。 [/編輯]

1

雖然我不知道很多的Python,我會說這就像任何其他語言:

只要沒有涉及到的變量已聲明的功能之外,因此可以共享線程,不應該需要鎖。而你的功能似乎不是這種情況。

輸出到控制檯可能會出現亂碼。

1

當您認爲您正在編寫的代碼是關鍵的代碼段時,即代碼段是否正在修改線程之間的共享狀態(如果不是,則不需要擔心鎖定)時,需要鎖定。

方法是否應該被鎖定是一種設計選擇,理想情況下,您應該鎖定更接近線程的共享狀態訪問。

1
  1. 在你的代碼中,你實現了你自己的spin-lock。雖然這是可能的,但我不認爲這是Python中的建議,因爲它可能會導致性能問題。

  2. 我使用了一個衆所周知的搜索引擎(從G開始),查詢「python lock」。第一個結果是這一個:Thread Synchronization Mechanisms in Python。它看起來像一個很好的文章開始。

  3. 對於代碼本身:只要在共享資源上執行的操作不是原子的,就應該鎖定。它目前看起來像你的代碼中沒有這樣的資源。

+1

'total'不是全球性的,雖然 – Sebastian 2013-03-14 07:59:52

+1

@塞巴斯蒂安感謝您的評論,改變了我的答案。 – 2013-03-14 12:32:04