互斥锁和线程的问题

时间:2011-09-15 11:21:10

标签: multithreading delphi mutex

我处于多线程情况,我有一个函数,我想一次只从一个线程运行。但是,我不希望以传统方式序列化函数,而是希望任何线程尝试进入函数,而第一个线程正在运行它以立即返回。我不希望第二个线程等待第一个线程。

这是我的代码:

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)

4 个答案:

答案 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;