【问题标题】:Looking for a Reader Writer Lock that works with async/await寻找适用于 async/await 的 Reader Writer Lock
【发布时间】:2020-05-30 08:08:47
【问题描述】:

我正在将一个旧应用程序移植到 .NET 核心。我需要一个读写器锁(许多读取操作,偶尔的写入操作)。我的旧代码是多线程的,所以旧的 ReaderWriterLock 工作得很好。新代码是基于任务的,所以我尝试采用异步/等待模式。

这让我意识到我需要锁定原语。旧的锁定原语仅在您等待单个线程时才有效。有一个任务感知的 SemaphoreSlim,但我不明白它如何用于 Reader Writer 锁。

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

【问题讨论】:

  • 特别是AsyncReaderWriterLock (/cc @xxbbcc)
  • 知道了。它似乎是我引用的同一个库,但我在哪里可以找到一些使用示例?
  • 注意:这是 Visual Studio 的一部分 The Microsoft.VisualStudio.Threading namespace contains APIs for use in Visual Studio SDK projects. You should use these APIs for asynchronous programming in Visual Studio. Doing so helps you avoid deadlocks

标签: .net multitasking


【解决方案1】:

如果我可以使用 Microsoft 的某些东西,我不想使用 Stephen Cleary 的库。

我也是。

那么,这里有什么故事呢?

VS-Threading library 有一点历史。它最初是 VS-SDK 的一部分,用于(并且仅许可用于)VS 扩展。这并没有阻止人们找到它并使用他们自己的应用程序分发它,我多次建议不要这样做。这些天来,它被分拆成自己的项目,可在 NuGet 上获得,并获得 MIT 许可。所以我认为这些天使用它很好。但这就是历史,我认为历史就是它没有被广泛采用的原因。

是否有支持读写器锁定的 .NET 多任务原语?

不作为框架的一部分(至少目前还没有)。有AsyncExVS-Threadingroll-your-own。我不确定 VS-Threading 在 Microsoft 支持方面存在于何处 - 即它是否由特定团队维护?我确实在最近的提交中看到了 Andrew Arnott 和 Sam Harwell,他们都是该领域的天才,所以我认为它现在掌握得很好。我只是不确定它是 Microsoft 项目的官方程度。

我是否有理由避免使用 AsyncReaderWriterLock?

当然。我通常反对读/写锁。原因是很多开发人员认为“我的一些代码读取,我的一些代码写入,所以我需要一个 RWL”,而事实并非如此。还有额外的要求:读取的数量必须大大超过写入的数量,并且读取必须至少有压倒写入的威胁。如果没有满足这些额外的要求,那么一个简单的锁就可以了。对于异步代码尤其如此;在执行 I/O 时持有锁并不常见。

有没有人看到它工作的例子?

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

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

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

【讨论】:

  • 1.) 感谢您对多任务原始景观的客观分析。 2.) 你的用法是正确的。我在 GitHub 站点的“问题”部分找到了一个示例。 3.) 我同意你关于支持的观点。 S. Harwell 正在贡献它的事实告诉我,该软件包背后一定有一些受人尊敬的社区。​​span>
  • 你能在 ASP.NET 或 ASP.NET Core 中使用它吗?它在 VisualStudio 命名空间中! The Microsoft.VisualStudio.Threading namespace contains APIs for use in Visual Studio SDK projects. 这似乎是不使用它的充分理由。
【解决方案2】:

聚会有点晚了,但是..

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

返回 ValueTask 的 ReadersWriterLock Async。所以当锁被直接取走时,你不必等待它。

此库将按请求的顺序处理锁。它还支持在其启动的 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/创建 nuget 包没有太多经验,所以欢迎提出任何想法)

包裹:nuget

来源:github

【讨论】:

    猜你喜欢
    • 2017-06-10
    • 1970-01-01
    • 1970-01-01
    • 2018-03-24
    • 1970-01-01
    • 2019-10-24
    • 2018-07-10
    • 1970-01-01
    • 2018-08-10
    相关资源
    最近更新 更多