title: Using ConcurrentExclusiveSchedulerPair as an async ReaderWriterLock equivalent
body: <p>My application has several async methods that can access files on disk. As I learned the ordinary <code>ReaderWriterLockSlim</code> cannot be used in such case, I went looking for an equivalent. Found the <code>ConcurrentExclusiveSchedulerPair</code> that looked very promising. I read a few enlightening blog posts about it and I started to think this could be the right choice for me. </p>
  
  <p>So I changed all my reading tasks to use the Concurrent scheduler and writing tasks to use the Exclusive one. However, it turned out I was still getting <code>IOException</code>s saying the file is in use. Here's a simplified (and still failing) version of my code:</p>
  
  <pre><code>public async Task&lt;IEnumerable&lt;string&gt;&gt; Run()
  {
      var schedulers = new ConcurrentExclusiveSchedulerPair();
      var exclusiveFactory = new TaskFactory(schedulers.ExclusiveScheduler);
      var concurrentFactory = new TaskFactory(schedulers.ConcurrentScheduler);
  
      var tasks = new List&lt;Task&gt;();
      for (var i = 0; i &lt; 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");
      }
  }
  </code></pre>
  
  <p>Then I found <a href="https://blog.stephencleary.com/2012/08/async-and-scheduled-concurrency.html" rel="nofollow noreferrer">Stephen Cleary's blog post</a> with a warning in a red box:</p>
  
  <blockquote>
    <p>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.</p>
  </blockquote>
  
  <p>"One task at a time, not one task until it completes" - now this is a bit confusing. Does this mean the <code>ConcurrentExclusiveSchedulerPair</code> is not the right choice for this situation or am I using it incorrectly? Perhaps an <code>AsyncLock</code> should be used here instead (why isn't it a part of the framework)? Or would a plain old <code>Semaphore</code> be enough (I understand I wouldn't get the reader-writer division then, but maybe it's ok)?</p>