7
votes

I have a common resource, which I want 1 and only 1 instance of my application (or it's COM API) to have access to at any time. I have tried to protect this resource using mutexes, but when multiple threads of a host dotnet application try to access the COM object, the mutex doesn't seem to be released. This is the code I have used to protect my resource.

repeat
  Mutex := CreateMutex(nil, True, PChar('Connections'));
until (Mutex <> 0) and (GetLastError <> ERROR_ALREADY_EXISTS);
  try
    //use resource here!
  finally
    CloseHandle(Mutex);
  end;

If I run the threads simultaneously, the first thread get's through (obviously, being the first one to create the mutex), but subsequent threads are caught in the repeat loop. If I run each thread at 5 second intervals, then all is ok.

I suspect I'm not using mutexes correctly here, but I have found very little documentation about how to do this.

Any ideas?

2

2 Answers

16
votes

You're using the mutex wrong. You should be waiting for it and releasing it, not recreating it constantly.

During initialization:

Mutex := CreateMutex(nil, False, 'Connections');
if Mutex = 0 then
  RaiseLastOSError;

When you want to access the resource

if WaitForSingleObject(Mutex, INFINITE) <> WAIT_OBJECT_0 then
  RaiseLastOSError;
try
  // Use resource here
finally
  ReleaseMutex(Mutex)
end;

During finalization

CloseHandle(Mutex);

Also, since mutexes are global, you should pick something a little more unique than "connections" for the name. We added a GUID to the end of ours.

13
votes

Try with this simple demo code. Start several instances of the application, and you can see from the background colour how they share the mutex:

procedure TForm1.FormCreate(Sender: TObject);
begin
  fMutex := SyncObjs.TMutex.Create(nil, False, 'GlobalUniqueName');
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  fMutex.Free;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  Color := clRed;
  Update;
  fMutex.Acquire;
  try
    Color := clGreen;
    Update;
    Sleep(5 * 1000);
  finally
    fMutex.Release;
  end;
  Color := clBtnFace;
end;

Note that I chose to use the TMutex class from the SyncObjs unit, which simplifies things.