[英]Async Task method WaitingForActivation
我發現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,然后是一些,兩個等等。
相反,請考慮以下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;
});
}
現在一切都與Console應用程序中的相同。
有人可以解釋控制台中的行為與UWP應用程序不同的原因嗎? 為什么在UWP應用程序中任務保持為c
?
我再次回到這個問題,但發現一個問題,原來接受的答案沒有涵蓋 - UWP上的代碼永遠不會到達 .Result
,它只是繼續檢查返回false
IsCompleted
,因此返回_lastResult
。 是什么讓Task
在應該完成時具有AwaitingActivation
狀態?
我發現原因是主動等待while
循環阻止await
繼續再次占用UI線程,因此導致類似“死鎖”的情況。
根據UWP應用程序中的代碼,不需要保留_calculateTask
。 await
任務。
這是更新的代碼
private static int _i = 0;
private static int _lastResult = 0;
public async Task<int> Calculate() {
_lastResult = await CalculateNextAsync();
return _lastResult;
}
//No need to wrap the code in a Task.Run. Just await the async code
private static async Task<int> CalculateNextAsync()
await Task.Delay(2000);
return ++_i;
}
//Event Handlers allow for async void
private async void Button_Click(object sender, RoutedEventArgs e) {
while( true) {
var result = await Calculate();
Debug.WriteLine(result.ToString());
}
}
你是混合異步/等待和阻塞樣來電.Result
在UWP的應用程序,這是造成,因為它的一個塊的SynchronizationContext的僵局。 控制台應用程序是該規則的一個例外,這就是它在那里工作而不是在UWP應用程序中工作的原因。
此死鎖的根本原因是await處理上下文的方式。 默認情況下,當等待未完成的任務時,捕獲當前的“上下文”並用於在任務完成時恢復該方法。 這個“上下文”是當前的SynchronizationContext,除非它是null,在這種情況下它是當前的TaskScheduler。 GUI和ASP.NET應用程序具有SynchronizationContext,一次只允許運行一個代碼塊。 當await完成時,它會嘗試在捕獲的上下文中執行異步方法的剩余部分。 但是該上下文已經有一個線程,它(同步)等待異步方法完成。 他們每個人都在等着對方,造成僵局。
請注意,控制台應用程序不會導致此死鎖。 它們有一個線程池SynchronizationContext而不是一次一個塊的SynchronizationContext,因此當await完成時,它會在線程池線程上調度async方法的其余部分。 該方法能夠完成,完成其返回的任務,並且沒有死鎖。 當程序員編寫測試控制台程序,觀察部分異步代碼按預期工作,然后將相同的代碼移動到GUI或ASP.NET應用程序中時,這種行為差異可能會令人困惑。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.