我发现了async
方法非常令人困惑的行为。请看以下控制台应用程序:
private static int _i = 0;
private static Task<int> _calculateTask = Task.FromResult(0);
private static int _lastResult = 0;
static void Main(string[] args)
{
while (true)
{
Console.WriteLine(Calculate());
}
}
private static int Calculate()
{
if (!_calculateTask.IsCompleted)
{
return _lastResult;
}
_lastResult = _calculateTask.Result;
_calculateTask = CalculateNextAsync();
return _lastResult;
}
private static async Task<int> CalculateNextAsync()
{
return await Task.Run(() =>
{
Thread.Sleep(2000);
return ++_i;
});
}
如预期的那样,在启动后,它首先会打印出一堆0,然后是1、2等。
相比之下,考虑以下UWP应用程序片段:
private static int _i = 0;
private static Task<int> _calculateTask = Task.FromResult(0);
private static int _lastResult = 0;
public int Calculate()
{
if (!_calculateTask.IsCompleted)
{
return _lastResult;
}
_lastResult = _calculateTask.Result;
_calculateTask = CalculateNextAsync();
return _lastResult;
}
private static async Task<int> CalculateNextAsync()
{
return await Task.Run( async() =>
{
await Task.Delay(2000);
return ++_i;
});
}
private void Button_Click(object sender, RoutedEventArgs e)
{
while( true)
{
Debug.WriteLine(Calculate());
}
}
尽管这两者只有一个小细节的不同,但UWP片段仅打印出0,且任务状态在
if
语句中仍保持为Waitingforactivation
。此外,可以通过从CalculateNextAsync
中移除async
和await
来解决问题。private static Task<int> CalculateNextAsync()
{
return Task.Run(async () =>
{
await Task.Delay(2000);
return ++_i;
});
}
现在一切的工作方式与控制台应用程序相同。
有人能解释一下为什么控制台和UWP应用程序的行为不同吗?为什么在UWP应用程序中任务保持为“c”?
更新
我又回到了这个问题,但发现了一个原先被接受的答案没有涵盖的问题 - UWP上的代码从来没有达到“.Result”,它只是不断检查“IsCompleted”,返回“false”,因此返回“_lastResult”。什么导致了“Task”在应该已经完成时具有“AwaitingActivation”状态?
解决方案
我发现问题的原因是活动等待“while”循环阻止了“await”继续占用UI线程,因此导致了类似于“死锁”的情况。
.Result
,它只是不断地检查IsCompleted
。为什么会这样? - Martin Zikmundwhile
循环永远不会允许继续占用UI线程,这使其“基本上”无法继续执行。 - Martin Zikmund