2011-09-15 128 views
2

我在多線程的情況下,我有一個函數,我想一次只從一個線程運行。但是,我不想以傳統的方式序列化函數,而是想讓所有嘗試進入函數的線程在第一個線程運行時立即返回。我不希望第二個線程等待第一個線程。互斥鎖和線程問題

這裏是我的代碼:

function InitMutex(const Name:String; var Handle: THandle):Boolean; 
begin 
Handle := CreateMutexA(NIL, True, PAnsiChar(Name)); 
Result := not (GetLastError = ERROR_ALREADY_EXISTS); 
end; 


procedure TForm1.Button1Click(Sender: TObject); 
var 
mHandle: THandle; 
begin 
if not InitMutex(BalloonTipMutex, mHandle) then Exit; 


MessageBox(0, 'Executing Code....', '', 0); 


ReleaseMutex(mHandle); 
CloseHandle(mHandle); 
end; 

這僅僅是同樣的問題爲例,因爲我不能做與線程的測試樣品。

問題是:我第一次點擊button1時出現messagebox,而messagebox仍然顯示(假設函數仍在運行)我再次按下button1,沒有任何顯示(這是應該發生的)但是當我關閉消息框並再次按下按鈕時,它什麼也不顯示。 (所謂的功能,因爲它沒有運行再次運行:S)

回答

3

試試這個:

procedure TForm1.Button1Click(Sender: TObject); 
var mHandle: THandle; 
begin 
    mHandle := 0; 
    if InitMutex(BalloonTipMutex, mHandle) then 
    begin  
    MessageBox(0, 'Executing Code....', '', 0); 
    ReleaseMutex(mHandle); 
    end; 
    if handle <> 0 then 
    CloseHandle(mHandle); 
end; 

你的問題是...即使CreateMutex返回錯誤ERROR_ALREADY_EXISTS,它沒有「開放」的互斥體。所以當你的第一個函數退出時,互斥體並沒有被釋放,因爲你的第二次調用打開了它,但從未關閉它。所以當你第三次嘗試調用你的函數時,它並不會因爲你的第一次調用保持互斥體打開,而是因爲你的第二次調用了。

另外,我覺得InitMutex應該返回Result := (Handle <> 0) and not (GetLastError = ERROR_ALREADY_EXISTS)

編輯:在一個側面說明,這是不是真的互斥方式是爲了使用。使用互斥鎖的「傳統」方法是創建它們,然後讓您的線程在您想要執行由互斥鎖保護的代碼時嘗試獲取它們的所有權。我期望CreateMutex比僅僅擁有一個互斥鎖要慢得多,也許這種技術還有一些其他的缺陷。

+0

謝謝,那完全沒有工作:D,那麼你認爲我應該在這種情況下使用什麼? – killercode

+0

互斥量對此非常重量級。很難相信這是最好的解決方案。 –

+0

@Killercode,我建議你爲此詢問一個單獨的問題。 –

3

現在我終於明白了這個問題,我相信最有效的解決方案就是使用互鎖操作。

procedure OneAtATimeThroughHere; 
//FLockCount is a properly aligned integer, shared between all threads 
var 
    ThisLockCount: Integer; 
begin 
    ThisLockCount := InterlockedIncrement(FLockCount); 
    try 
    if ThisLockCount=1 then//we won the race 
    begin 
     //do stuff 
    end; 
    finally 
    InterlockedDecrement(FLockCount); 
    end; 
end; 

該方法不允許重入呼叫。如果您需要迎合重新撥入電話,那麼解決方案是使用TryEnterCriticalSection()。關鍵部分比互斥更容易使用,而且速度也更快。 Delphi將關鍵部分API包裝在SyncObjs單元的TCriticalSection對象中。

所以,你的代碼應該是這樣的:

procedure OneAtATimeThroughHere; 
//FLock is an instance of TCriticalSection shared between all threads 
if FLock.TryEnter then 
begin 
    try 
    //do stuff 
    finally 
    FLock.Release; 
    end; 
end; 
+0

關於聯鎖方法,我可能會使用'InterlockedCompareExchange'來代替。否則,你的方法有(儘管不重要)失去呼叫的機會。 –

+0

@ken不,它不能失去電話 –

+0

那麼,AFAIK,這個例子是可能的:Thread1輸入函數(取得所有權)。線程2進入,只增加計數器。 Thread1退出函數。線程3進入函數(在線程2退出之前)。即使函數不是「擁有」,Thread3也不能擁有所有權,因爲Thread2對函數仍具有(false)所有權。 –

3

作爲一個替代的解決方案,你可以使用AddAtom()FindAtom()DeleteAtom() Windows API函數(見:http://msdn.microsoft.com/en-us/library/ms649056(v=vs.85).aspx)。還有這些在進程之間使用的全局版本。

使用原子將允許您保持對線程流的完全控制,並在函數內包含整個鎖定機制(就像您可以使用關鍵部分一樣)。

+0

+1這將工作正常。不需要FindAtom(),只需添加和刪除即可。 –

1

只要線程正在運行,您應該創建一次互斥鎖,然後讓函數使用WaitForSingleObject(),其超時時間爲0毫秒,以嘗試獲取互斥鎖。如果WaitForSingleObject()返回WAIT_OBJECT_0,那麼該函數尚未運行。

var 
    mHandle: THandle = 0; 

procedure TForm1.FormCreate(Sender: TObject); 
begin 
    mHandle := CreateMutex(nil, False, nil); 
end; 

procedure TForm1.FormDestroy(Sender: TObject); 
begin 
    CloseHandle(mHandle); 
end; 

procedure TForm1.Button1Click(Sender: TObject); 
begin 
    if WaitForSingleObject(mHandle, 0) = WAIT_OBJECT_0 then 
    begin 
    try 
     MessageBox(0, 'Executing Code....', '', 0); 
    finally 
     ReleaseMutex(mHandle); 
    end; 
    end; 
end;