SynchronizationLockException on Monitor.Exit when using await

async-await, c#, multithreading, synchronization, task-parallel-library

Solution

You can't `await` a task inside a `lock` scope (which is syntactic sugar for `Monitor.Enter` and `Monitor.Exit`). Using a `Monitor` directly will fool the compiler but not the framework.

`async-await` has no thread-affinity like a `Monitor` does. The code after the `await` will probably run in a different thread than the code before it. Which means that the thread that releases the `Monitor` isn't necessarily the one that acquired it.

Either don't use `async-await` in this case, or use a different synchronization construct like `SemaphoreSlim` or an `AsyncLock` you can build yourself. Here's mine: https://stackoverflow.com/a/21011273/885318

Problem

I am creating a piece of code that gets a webpage from a legacy system we have. In order to avoid excessive querying, I am caching the obtained URL. I am using `Monitor.Enter`, `Monitor.Exit` and double checking to avoid that request is issued twice, but when releasing the lock with `Monitor.Exit`, I am getting this exception: ``` System.Threading.SynchronizationLockException was caught HResult=-2146233064 Message=Object synchronization method was called from an unsynchronized block of code. Source=MyApp StackTrace: at MyApp.Data.ExProvider.<OpenFeature>d__0.MoveNext() in c:\Users\me\Documents\Visual Studio 2013\Projects\MyApp\MyApp\Data\ExProvider.cs:line 56 --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() at MyApp.Data.ExProvider.<GetSupportFor>d__15.MoveNext() in c:\Users\me\Documents\Visual Studio 2013\Projects\MyApp\MyApp\Data\ExProvider.cs:line 71 InnerException: ``` The line 56 is the `Monitor.Exit`. This is the code that performs the operation: ``` private async Task<Stream> OpenReport(String report) { var file = _directory.GetFiles(report+ ".html"); if (file != null && file.Any()) return file[0].OpenRead(); else { try { Monitor.Enter(_locker); FileInfo newFile = new FileInfo(Path.Combine(_directory.FullName, report + ".html")); if (!newFile.Exists) // Double check { using (var target = newFile.OpenWrite()) { WebRequest request = WebRequest.Create(BuildUrl(report)); var response = await request.GetResponseAsync(); using (var source = response.GetResponseStream()) source.CopyTo(target); } } return newFile.OpenRead(); } finally { Monitor.Exit(_locker); } } } ``` So what is the problem with `await` and `Monitor`? Is it because it is not the same thread when `Monitor.Enter` than when `Monitor.Exit`?

Original source

Related problems