如何取消异步处理的TransactionScope /等待? [英] How to dispose TransactionScope in cancelable async/await?
问题描述
我试图使用新的异步/等待功能异步用DB工作。由于有些要求可能会很长,我希望能够取消它们。我遇到的问题是,的TransactionScope
显然有一个线程亲和力,似乎取消该任务时,它的的Dispose()
被运行在一个错误的线程。
I'm trying to use the new async/await feature to asynchronously work with a DB. As some of the requests can be lengthy, I want to be able to cancel them. The issue I'm running into is that TransactionScope
apparently has a thread affinity, and it seems that when canceling the task, its Dispose()
gets ran on a wrong thread.
特别是,当调用 .TestTx()
我得到以下 AggregateException
包含出现InvalidOperationException
在 task.Wait()
:
Specifically, when calling .TestTx()
I get the following AggregateException
containing InvalidOperationException
on task.Wait ()
:
"A TransactionScope must be disposed on the same thread that it was created."
这里的code:
Here's the code:
public void TestTx () {
var cancellation = new CancellationTokenSource ();
var task = TestTxAsync ( cancellation.Token );
cancellation.Cancel ();
task.Wait ();
}
private async Task TestTxAsync ( CancellationToken cancellationToken ) {
using ( var scope = new TransactionScope () ) {
using ( var connection = new SqlConnection ( m_ConnectionString ) ) {
await connection.OpenAsync ( cancellationToken );
//using ( var command = new SqlCommand ( ... , connection ) ) {
// await command.ExecuteReaderAsync ();
// ...
//}
}
}
}
更新:注释掉的部分是要表明有事情做了 - 异步 - 有一次,它是开放的连接,但不要求code重现该问题
UPDATED: the commented out part is to show there's something to be done — asynchronously — with the connection once it's open, but that code is not required to reproduce the issue.
推荐答案
这个问题的原因是,我是原型在控制台应用程序中的code,我没有在这个问题反映了茎。
The problem stems from the fact that I was prototyping the code in a console application, which I did not reflect in the question.
办法异步/等待继续执行code后等待
取决于 SynchronizationContext.Current <的presence / code>和控制台应用程序还没有一个在默认情况下,这意味着使用当前被执行的延续
的TaskScheduler
,这是一个线程池
,所以它(的可能?的)执行在不同的线程。
The way async/await continues to execute the code after await
is dependent on the presence of SynchronizationContext.Current
, and console application don't have one by default, which means the continuation gets executed using the current TaskScheduler
, which is a ThreadPool
, so it (potentially?) executes on a different thread.
因此,人们只是需要有一个的SynchronizationContext
,这将确保的TransactionScope
设置在同一线程创建它。 WinForms和WPF应用都会有它在默认情况下,当控制台应用程序可以使用一个自定义的,或借WPF DispatcherSynchronizationContext
。
Thus one simply needs to have a SynchronizationContext
that will ensure TransactionScope
is disposed on the same thread it was created. WinForms and WPF applications will have it by default, while console applications can either use a custom one, or borrow DispatcherSynchronizationContext
from WPF.
下面是详细解释力学两个伟大的博客文章:
等待,SynchronizationContext的,和控制台应用
等待,SynchronizationContext的,和控制台程序:第2部分
Here are two great blog posts that explain the mechanics in detail:
Await, SynchronizationContext, and Console Apps
Await, SynchronizationContext, and Console Apps: Part 2
这篇关于如何取消异步处理的TransactionScope /等待?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!