【发布时间】:2018-02-14 07:59:10
【问题描述】:
我正在为 TransactionScope 对象的 using 块内做一些事情。在某些时候,我想通过触发并忘记来调用一些异步代码(我不想等待结果,而且我对该调用期间发生的事情不感兴趣)并且我希望该代码不属于交易(通过使用TransactionScopeOption.Suppress 选项)。
所以最初我做了一些类似于我在下面的代码中评论过的methodFails。它给了我一个很好的“System.InvalidOperationException:'TransactionScope 嵌套不正确'”。我在 SO 中查找了有类似问题的人,并发现了这个 Question,其中 ZunTzu 的答案给了我使用 TransactionScopeAsyncFlowOption.Enabled 选项的 method1 的想法,这与我对 methodFails 的预期一样有效,但没有例外。
然后我想到了一个替代方案,我将method2 放入其中,将异步代码放入第三种方法(method3)中,该方法由触发后调用调用,而TransactionScopeOption.Suppress 选项保留在非-异步method2。在我的示例程序中,这种方法似乎与method1 一样有效。
所以我的问题是:哪种方法更好,method1 或 method2,或者可能是我没想到的第三种方法?我倾向于method1,因为这听起来像是“制作 TransactionScope 类的人将 TransactionScopeAsyncFlowOption 放在那里是有原因的”。但是TransactionScopeAsyncFlowOption.Enabled 不是 TransactionScope 的默认值这一事实让我认为启用它可能会影响性能,而即发即弃可能是我可以保存该性能影响的一种特殊情况。
示例代码:
class Program
{
static void Main(string[] args)
{
using (TransactionScope scope1 = new TransactionScope())
{
// Do some stuff in scope1...
// Start calls that could execute async code
//Task a = methodFails(); // This commented method would launch exception: System.InvalidOperationException: 'TransactionScope nested incorrectly'
Task b = method1(); // Fire and forget
method2();
// Rest of stuff in scope1 ...
}
Console.ReadLine();
}
static async Task methodFails()
{
//Start of non-transactional section
using (TransactionScope scope2 = new TransactionScope(TransactionScopeOption.Suppress))
{
//Do non-transactional work here
Console.WriteLine("Hello World 0.1!!");
await Task.Delay(10000);
Console.WriteLine("Hello World 0.2!!");
}
//Restores ambient transaction here
Console.WriteLine("Hello World 0.3!!");
}
static async Task method1()
{
//Start of non-transactional section
using (TransactionScope scope2 = new TransactionScope(TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Enabled))
{
//Do non-transactional work here
Console.WriteLine("Hello World 1.1!!");
await Task.Delay(10000);
Console.WriteLine("Hello World 1.2!!");
}
//Restores ambient transaction here
Console.WriteLine("Hello World 1.3!!");
}
static void method2()
{
//Start of non-transactional section
using (TransactionScope scope2 = new TransactionScope(TransactionScopeOption.Suppress))
{
//Do non-transactional work here
Task ignored = method3(); // Fire and forget
}
//Restores ambient transaction here
Console.WriteLine("Hello World 2.2!!");
}
static async Task method3()
{
//Do non-transactional work here
Console.WriteLine("Hello World 2.1!!");
await Task.Delay(10000);
Console.WriteLine("Hello World 2.3!!");
}
}
【问题讨论】:
-
如果您只是想在事务范围之外的即发即弃中运行您的代码,我会说这是
Task.Run的一个很好的用例 -
@Kevin Gosse 我前段时间在 Stephen Cleary 的博客中读到 Task.Run 应该用于 CPU 绑定代码。在我的生产代码中,“Task.Delay”调用实际上是一个存储过程调用,需要几秒钟才能完成,所以我认为它更适合 async-await 场景
-
这种评论有时让我想知道那篇博客文章是否带来的伤害大于帮助。当您想将工作卸载到另一个线程时,
Task.Run很有用。这对于 I/O 绑定操作几乎没有意义,而且这是一个容易犯的错误,这就是 Stephen 做出近似“Task.Run = CPU bound”的原因。但这并不意味着你不应该在它帮助你的时候使用它,只要你了解它的作用 -
我对那篇文章的解释是“如果有一种(合理的)方法可以使用异步等待完成 I/O 工作,那就去吧”。就我而言,我最初发布的两个选项都不是解决问题的坏方法,并且 async-await 语法比 Task.Run 传达了正在完成的工作的性质。但我同意你的观点,值得评估所有可用的工具。
标签: c# .net async-await transactionscope fire-and-forget