gpt4 book ai didi

c# - 使用 ConcurrentExclusiveSchedulerPair 作为异步 ReaderWriterLock 等价物

转载 作者:行者123 更新时间:2023-11-30 13:35:41 29 4
gpt4 key购买 nike

我的应用程序有几个异步方法可以访问磁盘上的文件。当我了解到在这种情况下不能使用普通的 ReaderWriterLockSlim 时,我开始寻找等效的。找到看起来很有前途的 ConcurrentExclusiveSchedulerPair。我阅读了一些关于它的启发性博客文章,我开始认为这对我来说可能是正确的选择。

所以我将所有读取任务更改为使用并发调度程序,并将所有写入任务更改为使用独占调度程序。然而,事实证明我仍然收到 IOException 说文件正在使用中。这是我的代码的简化(但仍然失败)版本:

public async Task<IEnumerable<string>> Run()
{
var schedulers = new ConcurrentExclusiveSchedulerPair();
var exclusiveFactory = new TaskFactory(schedulers.ExclusiveScheduler);
var concurrentFactory = new TaskFactory(schedulers.ConcurrentScheduler);

var tasks = new List<Task>();
for (var i = 0; i < 40; ++i)
{
// Create some readers and (less) writers
if (i % 4 == 0)
{
var writeTask = exclusiveFactory.StartNew(WriteToFile).Unwrap();
tasks.Add(writeTask);
}
else
{
var readTask = concurrentFactory.StartNew(ReadFromFile).Unwrap();
tasks.Add(readTask);
}
}

await Task.WhenAll(tasks);
return _contents;
}

private async Task ReadFromFile()
{
using (var fileStream = new FileStream("file.txt", FileMode.OpenOrCreate, FileAccess.Read))
using (var reader = new StreamReader(fileStream))
{
await Task.Delay(500); // some other work
_contents.Add(await reader.ReadToEndAsync());
}
}

private async Task WriteToFile()
{
using (var fileStream = new FileStream("file.txt", FileMode.OpenOrCreate, FileAccess.Write))
using (var writer = new StreamWriter(fileStream))
{
await Task.Delay(500); // some other work
await writer.WriteLineAsync("Lorem ipsum");
}
}

然后我找到Stephen Cleary's blog post红框警告:

When an asynchronous method awaits, it returns back to its context. This means that ExclusiveScheduler is perfectly happy to run one task at a time, not one task until it completes. As soon as an asynchronous method awaits, it’s no longer the “owner” of the ExclusiveScheduler. Stephen Toub’s async-friendly primitives like AsyncLock use a different strategy, allowing an asynchronous method to hold the lock while it awaits.

“一次一个任务,在完成之前不是一个任务”——现在这有点令人困惑。这是否意味着 ConcurrentExclusiveSchedulerPair 不是这种情况的正确选择,还是我使用不当?或许应该在这里使用 AsyncLock(为什么它不是框架的一部分)?或者一个普通的旧 Semaphore 就足够了吗(我知道那时我不会得到读写器部门,但也许没关系)?

最佳答案

async 方法[1] 的一种思考方式是,它们在每个 await 点被拆分为任务。要使用您的示例:

private async Task WriteToFile()
{
using (var fileStream = new FileStream("file.txt", FileMode.OpenOrCreate, FileAccess.Write))
using (var writer = new StreamWriter(fileStream))
{
await Task.Delay(500); // some other work
await writer.WriteLineAsync("Lorem ipsum");
}
}

概念上分为三个任务:

  1. 创建 fileStreamwriter 并启动 Task.Delay(一些其他工作)的任务。
  2. 观察 Task.Delay 的结果并启动 WriteLineAsync 的任务。
  3. 观察 WriteLineAsync 的结果并处理 writerfileStream 的任务。

ConcurrentExclusiveSchedulerPair 是一个任务调度器,所以它的语义只适用于有任务运行代码的时候。当 WriteToFileExclusiveScheduler 一起运行时,它在任务 (1) 运行时持有独占调度程序锁。一旦 Task.Delay 代码启动,任务 (1) 就完成了,它会释放独占调度程序锁。在 500 毫秒延迟期间,独占调度程序锁保持。一旦该延迟完成,任务 (2) 就准备就绪并排队到 ExclusiveScheduler。然后它获取独占调度程序锁并完成它的(少量)工作。当任务 (2) 完成时,它还会释放独占调度程序锁。等等

任务调度程序旨在处理同步任务。 await 对它们有一些支持(即,TaskScheduler.Current 被自动捕获并用于从 await 恢复),但一般来说在处理异步任务时,它们没有预期的语义。每个 await 实际上是在告诉任务调度程序“这个(子)任务已完成”。

Does this mean the ConcurrentExclusiveSchedulerPair is not the right choice for this situation?

是的。 ConcurrentExclusiveSchedulerPair 不是正确的选择。

Perhaps an AsyncLock should be used here instead (why isn't it a part of the framework)?

SemaphoreSlim 支持异步锁定(使用更笨拙的语法)。但它可能不在 WP81 上——我不记得了(WaitAsync 是后来添加的)。要支持这么旧的平台,您必须使用 AsyncEx v4 或复制/粘贴 Stephen Toub's code .

I understand I wouldn't get the reader-writer division then, but maybe it's ok?

这不仅没问题,而且几乎可以肯定是更可取的。当你真的只需要一个轻量级锁时使用读/写锁是一个非常常见的错误。特别是,仅仅因为某些代码具有读取语义而其他代码具有写入语义不是使用 RWL 的充分理由。

[1] 出于效率原因,async 方法在 await 点被分解为代码块,但这些单独的代码块实际上并未被 Task 对象,除非存在 TaskScheduler

关于c# - 使用 ConcurrentExclusiveSchedulerPair 作为异步 ReaderWriterLock 等价物,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48346489/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com