使用等待时 Monitor.Exit 上的 SynchronizationLockException [英] SynchronizationLockException on Monitor.Exit when using await

查看:18
本文介绍了使用等待时 Monitor.Exit 上的 SynchronizationLockException的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在创建一段代码,用于从我们拥有的旧系统中获取网页.为了避免过度查询,我将获取到的 URL 缓存起来.我正在使用 Monitor.EnterMonitor.Exit 并仔细检查以避免发出两次请求,但是在使用 Monitor.Exit,我得到了这个异常:

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:UsersmeDocumentsVisual Studio 2013ProjectsMyAppMyAppDataExProvider.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:UsersmeDocumentsVisual Studio 2013ProjectsMyAppMyAppDataExProvider.cs:line 71
  InnerException: 

第 56 行是 Monitor.Exit.这是执行操作的代码:

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);
        }
    }
}

那么 awaitMonitor 有什么问题呢?是不是因为 Monitor.EnterMonitor.Exit 不是同一个线程?

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?

推荐答案

你不能在 lock 范围内 await 任务(这是 的语法糖>Monitor.EnterMonitor.Exit).直接使用 Monitor 会欺骗编译器,但不会欺骗框架.

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 没有像 Monitor 那样的线程亲和性.await 之后的代码可能会在与之前的代码不同的线程中运行.这意味着释放 Monitor 的线程不一定是获取它的线程.

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.

在这种情况下不要使用 async-await,或者使用不同的同步结构,如 SemaphoreSlim 或您可以构建的 AsyncLock你自己.这是我的:https://stackoverflow.com/a/21011273/885318

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

这篇关于使用等待时 Monitor.Exit 上的 SynchronizationLockException的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆