首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用ConcurrentExclusiveSchedulerPair作为异步ReaderWriterLock等效

使用ConcurrentExclusiveSchedulerPair作为异步ReaderWriterLock等效
EN

Stack Overflow用户
提问于 2018-01-19 17:28:37
回答 1查看 2.4K关注 0票数 4

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

因此,我将所有读取任务更改为使用并发调度程序,并将写入任务更改为使用独占任务。然而,事实证明,我仍然得到IOException说该文件正在使用。下面是我的代码的一个简化版本(并且仍然失败):

代码语言:javascript
复制
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");
    }
}

然后我在一个红色的盒子里找到了一个警告斯蒂芬·克利里的博客文章

当异步方法等待时,它会返回到它的上下文。这意味着ExclusiveScheduler非常乐意一次运行一个任务,而不是在完成之前运行一个任务。异步方法一旦等待,它就不再是ExclusiveScheduler的“所有者”。Stephen的异步友好原语(如AsyncLock )使用了不同的策略,允许异步方法在等待锁时保持锁。

“一次一个任务,在它完成之前没有一个任务”--现在这有点让人困惑。这是否意味着对于这种情况,ConcurrentExclusiveSchedulerPair不是正确的选择,还是我使用它不正确?也许这里应该使用AsyncLock (为什么它不是框架的一部分)?或者一个普通的老Semaphore就足够了(我知道那时我不会让读者-作家部门,但也许这是可以的)?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2018-01-20 02:09:20

async methods1的一种思维方式是在每个await点将它们分成任务。使用您的示例:

代码语言:javascript
复制
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实际上都在告诉任务调度程序“完成了这个(子)任务”。

这是否意味着ConcurrentExclusiveSchedulerPair不是这种情况下的正确选择?

是。ConcurrentExclusiveSchedulerPair不是正确的选择。

也许这里应该使用AsyncLock (为什么它不是框架的一部分)?

SemaphoreSlim支持异步锁定(使用更笨拙的语法)。但它可能不会出现在WP81上--我不记得了(后来添加了WaitAsync)。要支持这么老的平台,您必须使用AsyncEx v4或复制/粘贴斯蒂芬·图布密码

我知道我不想让读者和作家分开,但也许没关系?

这不仅是好的,而且几乎肯定是可取的。在您真正需要轻量级锁时使用读取器/写入器锁是一个非常常见的错误。特别是,仅仅因为有些代码具有读取语义,而其他代码具有写语义,这并不是使用RWL的充分理由。

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

票数 14
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/48346489

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档