命名为Mutex和wait
本文关键字:wait Mutex 命名为 | 更新日期: 2023-09-27 17:58:50
因此,我不能将线程仿射锁与async
一起使用-在运行多个进程时,如何保护我的资源?
例如,我有两个进程使用下面的Task:
public async Task<bool> MutexWithAsync()
{
using (Mutex myMutex = new Mutex(false, "My mutex Name"))
{
try
{
myMutex.WaitOne();
await DoSomething();
return true;
}
catch { return false; }
finally { myMutex.ReleaseMutex(); }
}
}
如果Mutex保护的方法是同步的,那么上面的代码会起作用,但对于async
,我会得到:
对象同步方法是从未同步的代码块调用的。
那么命名互斥对于异步代码来说是无用的吗?
您必须确保在某个线程上一致地访问互斥对象。你可以通过多种方式做到这一点:
- 不要在持有互斥对象的关键部分使用wait
- 在只有一个线程的
TaskScheduler
上调用互斥调用
可能是这样的:
await Task.Factory.StartNew(() => mutex.WaitOne(), myCustomTaskScheduler);
或者,您使用同步代码并将所有内容移动到线程池中。如果您只能访问异步版本的DoSomething
,请考虑仅对其结果调用Task.Wait
。你在这里会有点效率低下。可能很好。
我在异步方法中使用命名Mutex来控制只有一个进程调用它。另一个进程检查命名Mutex,如果不能创建新的命名Mutex,则退出。
我可以在异步方法中使用命名Mutex,因为操作系统只保证/控制操作系统中命名对象的一个实例。另外,我不使用应该在线程上调用的WaitOne/Release。
public async Task<bool> MutexWithAsync()
{
// Create OS-wide named object. (It will not use WaitOne/Release)
using (Mutex myMutex = new Mutex(false, "My mutex Name", out var owned))
{
if (owned)
{
// New named-object was created. We own it.
try
{
await DoSomething();
return true;
}
catch
{
return false;
}
}
else
{
// The mutex was created by another process.
// Exit this instance of process.
}
}
}
您可以使用二进制Semaphore而不是Mutex。信号量不需要由获取它的同一线程释放。这里的最大缺点是,如果应用程序在DoSomething()中崩溃或终止,信号量将不会释放,应用程序的下一个实例将挂起。请参阅放弃的命名信号量未释放
public async Task<bool> MutexWithAsync()
{
using (Semaphore semaphore = new Semaphore(1, 1, "My semaphore Name"))
{
try
{
semaphore.WaitOne();
await DoSomething();
return true;
}
catch { return false; }
finally { semaphore.Release(); }
}
}
我为您提供了一个有趣的解决方案。现在没有时间提供代码示例,所以如果我的描述不够,请告诉我,我会尝试提供代码。
你有两个问题。首先,正如您所指出的,AsyncMutex不具有线程亲和性。所以你不能用Mutex做一个。但是,您可以从计数为1的信号量中构建一个信号量,因为信号量也不具有线程亲和性。在C#中,Semaphore类可以被命名并跨进程边界使用。因此,第一个问题相当容易解决。
第二个问题是在"锁定"此AsyncMutex时不希望使用阻塞调用。好吧,您可以使用ThreadPool.RegisterWaitForSingleObject来注册一个回调,该回调将在Semaphore(一个WaitHandle)发出信号时执行。这是一个"异步等待"。使用TaskCompletionSource用一些代码将其封装起来,就可以很容易地在AsyncMutex上构建一个返回Task的WaitAnc方法。这两个想法应该可以很容易地在C#中实现一个名为AsyncMutex的跨进程。
请记住,与您会发现的其他AsyncMutex实现一样,这不会是一个递归互斥体(同一个"线程"可以多次锁定互斥体,只要它解锁互斥体的次数相同),因此在代码中必须注意不要导致死锁。
此保护与async/await完美配合:
public sealed class AsyncLock : IDisposable
{
readonly AutoResetEvent Value;
public AsyncLock(AutoResetEvent value, int milliseconds = Timeout.Infinite)
{
if (value == null)
throw new ArgumentNullException("value");
value.WaitOne(milliseconds);
Value = value;
}
void IDisposable.Dispose()
{
Value.Set();
}
}
private async Task TestProc()
{
var guard = new AutoResetEvent(true); // Guard for resource
using (new AsyncLock(guard)) // Lock resource
{
await ProcessNewClientOrders(); // Use resource
} // Unlock resource
}