正在寻找与 async/await 一起使用的 Reader 写入器锁

Looking for a Reader Writer Lock that works with async/await

我正在将旧应用程序移植到 .NET Core。我需要一个 reader 写锁(很多读操作,偶尔写操作)。我的旧代码是大量多线程的,所以旧的 ReaderWriterLock 工作得很好。新代码是基于任务的,所以我试图接受 async/await 模式。

这让我想到了锁定原语的需要。旧的锁定原语仅在您等待单个线程时才有效。有一个任务感知的 SemaphoreSlim,但我不知道如何将其用于 Reader 写入器锁。

我发现了这个:AsyncReaderWriterLock,但我找不到任何使用示例或任何似乎在使用它的人。如果我可以使用 Microsoft 的东西,我不想使用 Stephen Cleary 的库。那么,这里的故事是什么?是否有支持 reader 编写器锁定的 .NET 多任务原语?我应该避免使用 AsyncReaderWriterLock 有什么理由吗?有没有人看到它工作的例子?

I don't want to use Stephen Cleary's library if I can use something from Microsoft.

我也是。

So, what's the story here?

VS-Threading library 有一段历史。它最初是 VS-SDK 的一部分,旨在用于(并且仅授权用于)VS 扩展。这并没有阻止人们找到它并用他们自己的应用程序分发它,我曾多次建议不要这样做。如今,它已独立成自己的项目,可在 NuGet 上使用,并获得 MIT 许可。所以我觉得这几天用着没问题。但这就是历史,我认为历史是它没有被广泛采用的原因。

Is there a .NET multi-tasking primitive that supports reader writer locking?

不作为框架的一部分(至少,现在还不是)。有 AsyncEx, VS-Threading, and roll-your-own。我不确定 VS-Threading 在 Microsoft 支持方面存在于何处 - 即,它是否由特定团队维护?我确实在最近的提交中看到了 Andrew Arnott 和 Sam Harwell,他们都是这个领域的天才,所以我想说现在掌握得很好。我只是不确定它是多么正式的 Microsoft 项目。

Is there a reason I should avoid using AsyncReaderWriterLock?

当然可以。我通常反对 reader/writer 锁。原因是很多开发者认为"some of my code reads, and some of my code writes, and so I need a RWL",其实不然。还有其他要求:读取的数量必须远远超过写入的数量,并且读取的数量必须至少有压倒写入的数量。如果不满足这些附加要求,那么一个简单的锁就可以了。对于异步代码尤其如此;在执行 I/O.

时持有锁并不常见

Has anyone seen an example of it working?

其实我没有,这有点好笑。但是看着 the source,我会说调用 await ReadLockAsync/await WriteLockAsync 然后处理结果值以释放锁。例如:

using (var releaser = await arwl.ReadLockAsync())
{
  ... // code using await
}

AsyncEx 和 roll-your-own 方法的用法相同。

晚会有点晚了,但是..

我也不得不处理这个问题。所有解决方案都使用 Tasks 而不是 ValueTask 结合 IDisposable 释放器。 (所以你总是要等待它) 如果不需要,我宁愿不等待就尝试获取锁。所以我想出了一个不同的方法,将一个函数传递给它,该函数将在锁内执行。

一个 ReadersWriterLock Async returns 一个 ValueTask。所以当直接拿锁的时候,就不用等待了。

此库将按请求的顺序处理锁。它还支持在启动它的 SynchronizationContext 上继续。 (对 UI 个线程有用)

我同意 “异步代码尤其如此;在执行 I/O 时持有锁并不常见。”

事实是,您可以使用异步代码等待锁中的代码执行完毕,并在需要时等待它。这并不意味着锁中的代码必须是异步的。


这是一个例子:

class Program
{
    static async Task Main(string[] args)
    {
        var rwl = new AsyncReadersWriterLock();

        Console.WriteLine("* Example 1");

        // run example 1
        var result1 = Example1(rwl);

        if (!result1.IsCompleted)
            await result1;

        Console.WriteLine("");

        Console.WriteLine("* Example 2");

        // run example 2
        var result2 = Example2(rwl);

        if (!result2.IsCompleted)
            await result2;
    }

    private static async ValueTask Example1(AsyncReadersWriterLock rwl)
    {
        Console.WriteLine("Before calling UseReaderAsync");
        var result = rwl.UseReaderAsync(async () =>
        {
            Console.WriteLine("Reader start");
            await Task.Delay(1000);
            Console.WriteLine("Reader end");
        });
        Console.WriteLine("After calling UseReaderAsync");
        Console.WriteLine("");

        if (!result.IsCompleted)
        {
            Console.WriteLine("result.IsCompleted == false, awaiting");
            await result;
            Console.WriteLine("await finished");
        }
        else
            Console.WriteLine("result.IsCompleted == true, no await is used");
    }

    private static async ValueTask Example2(AsyncReadersWriterLock rwl)
    {
        Console.WriteLine("Before calling UseReaderAsync");
        var result = rwl.UseReaderAsync(() =>
        {
            Console.WriteLine("Reader start");
            // no async is used
            //await Task.Delay(1000);
            Console.WriteLine("Reader end");
        });
        Console.WriteLine("After calling UseReaderAsync");
        Console.WriteLine("");

        if (!result.IsCompleted)
        {
            Console.WriteLine("result.IsCompleted == false, awaiting");
            await result;
            Console.WriteLine("await finished");
        }
        else
            Console.WriteLine("result.IsCompleted == true, no await is used");
    }
}

导致:

* Example 1
Before calling UseReaderAsync
Reader start
After calling UseReaderAsync

result.IsCompleted == false, awaiting
Reader end
await finished

* Example 2
Before calling UseReaderAsync
Reader start
Reader end
After calling UseReaderAsync

result.IsCompleted == true

我还在努力。 (我对 github/creating nuget 包没有太多经验,所以欢迎任何想法)

包裹:nuget

来源:github