我处于多线程情况,我有一个函数,我想一次只从一个线程运行。但是,我不希望以传统方式序列化函数,而是希望任何线程尝试进入函数,而第一个线程正在运行它以立即返回。我不希望第二个线程等待第一个线程。
这是我的代码:
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,消息框出现,同时仍然显示消息框(假设该功能仍在运行)我再次按下按钮1,没有显示任何内容(这应该是什么)但是当我关闭消息框并再次按下按钮时,它什么也没显示。 (该函数应该在未运行时再次运行:S)
答案 0 :(得分: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比只获取互斥锁的所有权要慢一些,并且可能还有其他一些陷阱。
答案 1 :(得分: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包装了SyncObjs单元中TCriticalSection
对象中的关键部分API。
所以你的代码看起来像这样:
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;
答案 2 :(得分:3)
作为替代解决方案,您可以使用AddAtom()
,FindAtom()
和DeleteAtom()
Windows API函数(请参阅:http://msdn.microsoft.com/en-us/library/ms649056(v=vs.85).aspx)。还有这些全局版本可供进程之间使用。
使用原子可以让你保持对线程流的完全控制,并在函数中包含整个锁定机制(就像使用临界区一样)。
答案 3 :(得分: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;