简体   繁体   English

这两个异步实现之间的区别

[英]Difference between these two async implementations

I am using EF's async methods to fetch data from a database. 我正在使用EF的异步方法从数据库中获取数据。 Most of the time all is well. 大部分时间都很好。 I have come into a few ObjectContextDisposed exceptions of late, and I am curious as to why my solution works: 我最近遇到了一些ObjectContextDisposed异常,我很好奇为什么我的解决方案有效:

Here was my original code that threw ObjectContextDisposed : 这是我抛出ObjectContextDisposed原始代码:

public Task<List<string>> GetEventParameterMru(EventParameter parameter, int count = 20)
{
    using (var repo = new ConfigurationRepository())
    {
        return repo.GetEventParameterMRU(_CurrentWorkpack, parameter, count)             
    }
}

Here is my new code that doesn't throw: 这是我的新代码,不抛出:

public async Task<List<string>> GetEventParameterMru(EventParameter parameter, int count = 20)
{
    using (var repo = new ConfigurationRepository())
    {
        var result = await repo.GetEventParameterMRU(_CurrentWorkpack, parameter, count);
        return result;
    }
}

Could someone explain to me what the difference is, and why it works? 有人可以向我解释有什么不同,为什么它有效?

Just FYI, in all my usages of this method, I call await GetEventParameterMru() 仅供参考,在我使用此方法的所有用法中,我调用await GetEventParameterMru()

Thanks 谢谢

GetEventParameterMRU is apparently a method that starts a Task to retrieve some data. GetEventParameterMRU显然是一个启动Task来检索某些数据的方法。 So GetEventParameterMRU returns before all operations on the repo have been completed. 因此, GetEventParameterMRUrepo上的所有操作完成之前返回。

Both version of your code use a using statement, which is translated into a try/finally block. 两个版本的代码都使用using语句,该语句被转换为try/finally块。 In the finally block, repo will be disposed . finally块中, repo将被处置

In your first version, you return immediatly after calling GetEventParameterMRU (starting the task). 在您的第一个版本中,您在调用GetEventParameterMRU (启动任务)后立即返回。 That means that repo is disposed immediatly while the Task using repo is still running. 这意味着当使用repoTask仍在运行时, repo立即处理。 So when this Task accesses repo you receive your 因此,当此Task访问repo您将收到您的repo

ObjectDisposedException 的ObjectDisposedException


In the second version you use await . 在第二个版本中,您使用await The compiler therefor translates your whole method into a state machine . 因此,编译器将您的整个方法转换为状态机 The method returns control to its caller at the await statement, but without passing the finally block. 该方法在await语句中将控制权返回给其调用者,但传递finally块。
When the Task completes, execution of your method is continued after the await statement. Task完成时,在await语句之后继续执行您的方法。
So repo is only disposed when the Task has completed . 所以repo 只在Task完成时处理 So you don't get an ObjectDisposedException . 所以你没有得到ObjectDisposedException

Following is my understanding on initial code review: 以下是我对初始代码审查的理解:

In first one, which is not Async implementation but still a Task is returned from the using block , which would call the repo.Dispose() in the end, when finally Task is executed (assuming its done by the caller, but holds true even for the method - GetEventParameterMRU starting it), it's quite feasible that repo is disposed by that time, but still operation is on and thus the exception 在第一个,它不是Async实现,但仍然从using block返回一个Task,最后Task is executed时调用repo.Dispose() (假设它由调用者完成,但仍然保持为真对于方法 - GetEventParameterMRU启动它,在那个时间处置repo是非常可行的,但仍然是操作,因此异常

In second one, that's not the case, even when it has freed the UI / Calling thread, until and unless following completes: 在第二个中,情况并非如此,即使它已经释放了UI /调用线程,直到并且除非完成:

await repo.GetEventParameterMRU(_CurrentWorkpack, parameter, count)

It will not call repo.Dispose() and thus completely avoid the ObjectContextDisposed issue 它不会调用repo.Dispose() ,因此完全避免了ObjectContextDisposed问题

Let me translate them for you: 让我为你翻译一下:

First one: 第一:

Create repository
Start GetEventParameterMRU on repository as Task
Dispose repository
return Task, that is still working with repository

Second one: 第二个:

Create repository
Start GetEventParameterMRU on repository as Task
Wait for Task to finish to get result
Dispose repository
return result

As you can see, the problem is quite clear here. 如您所见,这里的问题非常清楚。

I believe this has to do with the first code not capturing the current SynchronizationContext explicitly, while await DOES capture this explicitly. 我相信这与第一个没有显式捕获当前SynchronizationContext的代码有关,而等待DOES显式捕获它。

Some further reading: 进一步阅读:

https://msdn.microsoft.com/en-us/magazine/gg598924.aspx https://msdn.microsoft.com/en-us/magazine/gg598924.aspx

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM