使用 Async Await 实现全局互斥锁

9

我有一段关键的代码,只能由一个进程运行。为了确保机器上只有一个进程运行这个关键块,我使用一个全局互斥锁(Mutex)。关键部分的代码使用了async/await。总体来说,代码大致如下:

var mutex = new Mutex(false, @"Global\MyMutexName");    
mutex.WaitOne(-1, false);   
SetupASystemWideResource() 
await DoSomeAsyncWorkOnThatResource();    
mutex.ReleaseMutex();

问题在于await后面的代码可能会在不同的线程上执行。因此,互斥锁可能会在不同于获取它的线程上被释放。这会导致异常。
System.ApplicationException: Object synchronization method was called from an unsynchronized block of code.

通常情况下,这是有道理的,因为您不希望一个不同的线程释放互斥锁而不是获取它。但是在我的情况下,我只是想防止第二个应用程序实例在第一个实例使用它时以不同的方式设置系统资源。该资源本身可以在同一实例中的多个线程中使用。

正确的方法是如何确保这一点并异步地完成工作呢?


你能在UI线程上做到这一点吗?(假设是WinForms/WPF) - Alexei Levenkov
2个回答

4

如果我理解问题正确,而且这是一个控制台应用程序或没有同步上下文,只需调用Wait,就不会出现死锁的风险,它应该可以正常工作。

var mutex = new Mutex(false, @"Global\MyMutexName");    
mutex.WaitOne(-1, false);

DoSomeAsyncWorkOnThatResource().Wait();
mutex.ReleaseMutex();

或者如果你真的必须这样做(尽管有争议),你可以使用

Task.Run(async () => await DoSomeAsyncWorkOnThatResource()).Wait();

您可以使用自定义调度器,例如像CurrentThreadTaskScheduler(我第一次看到您使用它),如Stephen Toubs所述,Parallel Programming with .NET。Nuget ParallelExtensionsExtras

在请求调度时,在当前线程上运行所有任务

然后将其与Task.Factory.StartNew或接受调度器的其他内容一起使用


我发现DoSomeAsyncWorkOnThatResource().Wait();是解决await问题的好方法。 - undefined

3
一个信号量(Semaphore)没有必须由同一个线程释放的要求。因此,即使异步方法在不同的线程上继续执行,下面的代码也能够正常工作。但这里有个巨大的缺点,如果应用程序在工作过程中崩溃或被杀死,则信号量将无法被释放,因此下一次运行应用程序时会卡住。请参见Abandoned named semaphore not released
var semaphore = new Semaphore(1, 1, @"Global\MySemaphoreName");    
semaphore.WaitOne(-1, false);   
SetupASystemWideResource() 
await DoSomeAsyncWorkOnThatResource();    
semaphore.Release();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接