命名为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,我会得到:

对象同步方法是从未同步的代码块调用的。

那么命名互斥对于异步代码来说是无用的吗?

命名为Mutex和wait

您必须确保在某个线程上一致地访问互斥对象。你可以通过多种方式做到这一点:

  1. 不要在持有互斥对象的关键部分使用wait
  2. 在只有一个线程的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
}