C# 异步CTP和;最后";
代码如下:C# 异步CTP和;最后";,c#,.net,c#-4.0,async-ctp,C#,.net,C# 4.0,Async Ctp,代码如下: static class AsyncFinally { static async Task<int> Func( int n ) { try { Console.WriteLine( " Func: Begin #{0}", n ); await TaskEx.Delay( 100 ); Console.WriteLine( " Func:
static class AsyncFinally
{
static async Task<int> Func( int n )
{
try
{
Console.WriteLine( " Func: Begin #{0}", n );
await TaskEx.Delay( 100 );
Console.WriteLine( " Func: End #{0}", n );
return 0;
}
finally
{
Console.WriteLine( " Func: Finally #{0}", n );
}
}
static async Task Consumer()
{
for ( int i = 1; i <= 2; i++ )
{
Console.WriteLine( "Consumer: before await #{0}", i );
int u = await Func( i );
Console.WriteLine( "Consumer: after await #{0}", i );
}
Console.WriteLine( "Consumer: after the loop" );
}
public static void AsyncTest()
{
Task t = TaskEx.RunEx( Consumer );
t.Wait();
Console.WriteLine( "After the wait" );
}
}
如您所见,finally块的执行时间比您预期的要晚得多
有解决办法吗
提前谢谢 编辑
请考虑Theo Yaung的.
原始答案
我对async/await不太熟悉,但读完本文后:
在阅读代码时,我在Func(int n)
函数中看到了wait
,这意味着从wait
关键字后面的代码开始,直到函数结束,都将作为委托执行
所以我的猜测(这是一个未经教育的猜测)是Func:Begin
和Func:End
可能会在不同的“上下文”(线程?)中执行,即异步执行
因此,intu=wait Func(i)当到达Func
中的代码wait
时,Consumer
中的代码>行将继续执行。因此,很可能有:
Consumer: before await #1
Func: Begin #1
Consumer: after await #1
Consumer: before await #2
Func: Begin #2
Consumer: after await #2
Consumer: after the loop
Func: End #1 // Can appear at any moment AFTER "after await #1"
// but before "After the wait"
Func: Finally #1 // will be AFTER "End #1" but before "After the wait"
Func: End #2 // Can appear at any moment AFTER "after await #2"
// but before "After the wait"
Func: Finally #2 // will be AFTER "End #2" but before "After the wait"
After the wait // will appear AFTER the end of all the Tasks
Func:End
和Func:Finally
可以出现在日志中的任何位置,唯一的限制是Func:End#X
将出现在其关联的Func:Finally#X
之前,并且这两者都应该出现在等待后的之前
正如Henk Holterman(有点突然)所解释的,您在Func
主体中放置了一个wait
,这意味着后面的一切有时都会在后面执行
在函数的开始
和结束
之间放置了一个等待
,因此没有解决办法
就我那没受过教育的2欧分。这是一个极好的捕获-我同意CTP中实际上有一个bug。我仔细研究了一下,结果如下:
这是异步编译器转换的CTP实现,以及.NET4.0+中TPL(任务并行库)的现有行为的组合。以下是起作用的因素:
源代码中的finally主体被转换为实际CLR finally主体的一部分。出于许多原因,这是可取的,其中一个原因是我们可以让CLR执行它,而无需额外捕获/重试异常。这也在某种程度上简化了我们的代码生成—简单的代码生成在编译后会产生更小的二进制文件,这是我们的许多客户绝对希望的。:)李>
Func(int n)
方法的首要任务是真正的第三方物流任务。当您在Consumer()
中等待时,Consumer()
方法的其余部分实际上是作为从Func(int n)
返回的任务完成后的继续安装的
CTP编译器转换异步方法的方式导致return
在实际返回之前映射到SetResult(…)
调用<代码>设置结果(…)
归结为对的调用
TaskCompletionSource.TrySetResult
表示TPL任务完成。立即使其继续发生“某个时候”。这个“有时”可能意味着在另一个线程上,或者在某些情况下,TPL是聪明的,并且说“嗯,我现在可以在同一个线程上调用它”
Func(int n)
的首要任务
在最终运行之前就在技术上“完成”。这意味着在异步方法上等待的代码可以在并行线程中运行,甚至在finally块之前运行
考虑到首要的任务
应该表示方法的异步状态,基本上,在至少所有用户提供的代码都按照语言设计执行之前,它不应该被标记为已完成。我将与Anders、语言设计团队和编译器开发人员一起讨论这个问题
表现范围/严重程度:
static async Task<int> Func( int n )
{
int result;
try
{
Func<Task<int>> helperLambda = async() => {
Console.WriteLine( " Func: Begin #{0}", n );
await TaskEx.Delay( 100 );
Console.WriteLine( " Func: End #{0}", n );
return 0;
};
result = await helperLambda();
}
finally
{
Console.WriteLine( " Func: Finally #{0}", n );
}
// since Func(...)'s return statement is outside the try/finally,
// the finally body is certain to execute first, even in face of this bug.
return result;
}
static async Task<int> Func(int n)
{
int result;
try
{
result = await HelperMethod(n);
}
finally
{
Console.WriteLine(" Func: Finally #{0}", n);
}
// since Func(...)'s return statement is outside the try/finally,
// the finally body is certain to execute first, even in face of this bug.
return result;
}
static async Task<int> HelperMethod(int n)
{
Console.WriteLine(" Func: Begin #{0}", n);
await TaskEx.Delay(100);
Console.WriteLine(" Func: End #{0}", n);
return 0;
}
在WPF或WinForms的情况下,如果您有某种托管消息循环,您通常不会被这一点咬到。原因是任务
上的等待
的实现取决于。这将导致异步延续在预先存在的消息循环上排队,并在同一线程上运行。您可以通过以下方式将代码更改为运行Consumer()
来验证这一点:
DispatcherFrame frame = new DispatcherFrame(exitWhenRequested: true);
Action asyncAction = async () => {
await Consumer();
frame.Continue = false;
};
Dispatcher.CurrentDispatcher.BeginInvoke(asyncAction);
Dispatcher.PushFrame(frame);
在WPF消息循环的上下文中运行后,输出将按预期显示:
Consumer: before await #1
Func: Begin #1
Func: End #1
Func: Finally #1
Consumer: after await #1
Consumer: before await #2
Func: Begin #2
Func: End #2
Func: Finally #2
Consumer: after await #2
Consumer: after the loop
After the wait
解决方法:
static async Task<int> Func( int n )
{
int result;
try
{
Func<Task<int>> helperLambda = async() => {
Console.WriteLine( " Func: Begin #{0}", n );
await TaskEx.Delay( 100 );
Console.WriteLine( " Func: End #{0}", n );
return 0;
};
result = await helperLambda();
}
finally
{
Console.WriteLine( " Func: Finally #{0}", n );
}
// since Func(...)'s return statement is outside the try/finally,
// the finally body is certain to execute first, even in face of this bug.
return result;
}
static async Task<int> Func(int n)
{
int result;
try
{
result = await HelperMethod(n);
}
finally
{
Console.WriteLine(" Func: Finally #{0}", n);
}
// since Func(...)'s return statement is outside the try/finally,
// the finally body is certain to execute first, even in face of this bug.
return result;
}
static async Task<int> HelperMethod(int n)
{
Console.WriteLine(" Func: Begin #{0}", n);
await TaskEx.Delay(100);
Console.WriteLine(" Func: End #{0}", n);
return 0;
}
唉,解决方法是将代码更改为在try/finally
块中不使用return
语句。我知道这真的意味着您在代码流中失去了很多优雅。您可以使用异步助手方法或助手lambda来解决此问题。就个人而言,我更喜欢helper lambdas,因为它会自动关闭包含方法中的局部变量/参数,并使相关代码更接近
辅助Lambda方法:
static async Task<int> Func( int n )
{
int result;
try
{
Func<Task<int>> helperLambda = async() => {
Console.WriteLine( " Func: Begin #{0}", n );
await TaskEx.Delay( 100 );
Console.WriteLine( " Func: End #{0}", n );
return 0;
};
result = await helperLambda();
}
finally
{
Console.WriteLine( " Func: Finally #{0}", n );
}
// since Func(...)'s return statement is outside the try/finally,
// the finally body is certain to execute first, even in face of this bug.
return result;
}
static async Task<int> Func(int n)
{
int result;
try
{
result = await HelperMethod(n);
}
finally
{
Console.WriteLine(" Func: Finally #{0}", n);
}
// since Func(...)'s return statement is outside the try/finally,
// the finally body is certain to execute first, even in face of this bug.
return result;
}
static async Task<int> HelperMethod(int n)
{
Console.WriteLine(" Func: Begin #{0}", n);
await TaskEx.Delay(100);
Console.WriteLine(" Func: End #{0}", n);
return 0;
}
静态异步任务函数(int n)
{
int结果;
尝试
{
Func helperLambda=async()=>{
WriteLine(“Func:Begin{0}”,n);
等待TaskEx.延迟(100);
Console.WriteLine(“Func:End{0}”,n);
返回0;
};
结果=等待帮助程序lambda();
}
最后
{
WriteLine(“Func:Finally{0}”,n);
}
//由于Func(…)的return语句在try/finally之外,
//即使面对这个bug,finally主体也肯定会首先执行。
返回结果;
}
辅助方法方法:
static async Task<int> Func( int n )
{
int result;
try
{
Func<Task<int>> helperLambda = async() => {
Console.WriteLine( " Func: Begin #{0}", n );
await TaskEx.Delay( 100 );
Console.WriteLine( " Func: End #{0}", n );
return 0;
};
result = await helperLambda();
}
finally
{
Console.WriteLine( " Func: Finally #{0}", n );
}
// since Func(...)'s return statement is outside the try/finally,
// the finally body is certain to execute first, even in face of this bug.
return result;
}
static async Task<int> Func(int n)
{
int result;
try
{
result = await HelperMethod(n);
}
finally
{
Console.WriteLine(" Func: Finally #{0}", n);
}
// since Func(...)'s return statement is outside the try/finally,
// the finally body is certain to execute first, even in face of this bug.
return result;
}
static async Task<int> HelperMethod(int n)
{
Console.WriteLine(" Func: Begin #{0}", n);
await TaskEx.Delay(100);
Console.WriteLine(" Func: End #{0}", n);
return 0;
}
静态异步任务函数(int n)
{
int结果;
尝试
{
结果=等待帮助方法(n);
}
最后
{
WriteLine(“Func:Finally{0}”,n);
}
//由于Func(…)的return语句在try/finally之外,
//最后的身体肯定会