问题描述
我正在创建一段代码,用于从我们拥有的旧系统中获取网页.为了避免过度查询,我将获取到的 URL 缓存起来.我正在使用 Monitor.Enter
、Monitor.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);
}
}
}
那么 await
和 Monitor
有什么问题呢?是不是因为 Monitor.Enter
和 Monitor.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.Enter
和 Monitor.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的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!