[英]Why on Ctrl+C my process suddenly stopped on “Task<T>.Result”, but works fine with “await Task<T>”?
請幫助了解發生了什么。
我有async
方法。 它返回Task<T>
。
我需要等待此方法結果,但按 Ctrl+C 正確停止。
問題是:
如果我以這種方式等待 -一切正常:
var result = **await Task<T>**
如果我以這種方式等待 -主進程突然停止:
var result = **Task<T>.Result**
為了在代碼中顯示它,我寫了一個小測試(對不起 - 我不能讓它更小)
using System;
using System.Threading.Tasks;
namespace StopTest4
{
internal static class ConsoleApp
{
private static readonly TaskCompletionSource<bool> GetFirstString = new TaskCompletionSource<bool>();
private static void OnCancelKeyPressed(object sender, ConsoleCancelEventArgs args)
{
Console.WriteLine("Got Stop Signal (by Crt+C)");
GetFirstString.TrySetResult(false);
args.Cancel = true;
}
public static int Main(string[] args)
{
try
{
var appTask = RunAsync();
Console.CancelKeyPress += OnCancelKeyPressed;
return appTask.Result;
}
catch (Exception e)
{
Console.WriteLine($"Got exception while run main task of the App - {e}");
}
return -2;
}
private static async Task<int> RunAsync()
{
Console.WriteLine("RunAsync START");
try
{
await LoadWorksFine().ConfigureAwait(false);
//await LoadWithProcessSuddenlyClose().ConfigureAwait(false);
}
catch (Exception e)
{
Console.WriteLine($"Got Exception - {e}");
}
Console.WriteLine("Stopped!");
return 0;
}
private static Task LoadWithProcessSuddenlyClose()
{
try
{
Console.WriteLine($"Befor LoadString");
var result = LoadString().Result;
Console.WriteLine($"After LoadString - result=\"{result}\"");
}
catch (Exception e)
{
Console.WriteLine($"LOAD: Got Exception - {e}");
}
return Task.CompletedTask;
}
private static async Task LoadWorksFine()
{
try
{
Console.WriteLine($"Befor LoadString");
var result = await LoadString();
Console.WriteLine($"After LoadString - result=\"{result}\"");
}
catch (Exception e)
{
Console.WriteLine($"LOAD: Got Exception - {e}");
}
}
private static async Task<string> LoadString()
{
Console.WriteLine($"LoadString: Start");
try
{
if (!await GetFirstString.Task.ConfigureAwait(false))
{
Console.WriteLine($"LoadString: waited task Failed!");
return "false";
}
Console.WriteLine($"LoadString: waited task Success!");
return "true";
}
catch (Exception e)
{
Console.WriteLine($"LoadString: Got Exception - {e}");
}
return null;
}
}
}
如何檢查:
如果您運行測試並按 Ctrl+C,您將看到這樣的輸出:
% dotnet StopTest4.dll
RunAsync START
Befor LoadString
LoadString: Start
^CGot Stop Signal (by Crt+C) <---- Here press Ctrl+C
LoadString: waited task Failed!
After LoadString - result="false"
Stopped!
%
一切正常!
現在在方法RunAsync
注釋LoadWorksFine()
並取消注釋LoadWithProcessSuddenlyClose()
。 像這樣:
private static async Task<int> RunAsync()
{
Console.WriteLine("RunAsync START");
try
{
//await LoadWorksFine().ConfigureAwait(false);
await LoadWithProcessSuddenlyClose().ConfigureAwait(false);
}
catch (Exception e)
{
Console.WriteLine($"Got Exception - {e}");
}
Console.WriteLine("Stopped!");
return 0;
}
現在運行測試並按 Ctrl+C 你會看到這樣的輸出:
% dotnet StopTest4.dll
RunAsync START
Befor LoadString
LoadString: Start
^C <---- Here press Ctrl+C
%
沒有任何異常或錯誤 - 只是進程停止了。
它適用於 MacOS、Linux 和 Windows。
誰知道為什么? 至於我 - 兩種變體都是正確的。
謝謝!
PS:我想要第二個變體 - 我需要在同步方法中調用異步方法。
我在 dotnet git 上發布了這個問題。
並得到了答案:
因為您的示例同步阻塞 RunAsync 方法等待調用處理程序,但此時您還沒有連接處理程序。 因此,當按下 ctrl-C 時,沒有取消 ctrl-C 的處理程序,操作系統會終止該進程。
是的 - 在這段代碼中:
public static int Main(string[] args)
{
try
{
var appTask = RunAsync();
Console.CancelKeyPress += OnCancelKeyPressed;
return appTask.Result;
}
catch (Exception e)
{
Console.WriteLine($"Got exception while run main task of the App - {e}");
}
return -2;
}
行Console.CancelKeyPress += OnCancelKeyPressed;
從來沒有叫過。
如果把它放在前var appTask = RunAsync();
在這兩種變體中一切正常。
但現在我不明白為什么在第二個變體Console.CancelKeyPress += OnCancelKeyPressed;
從來沒有叫過。
據我了解, var appTask = RunAsync();
只創建任務。
在這里等待的任務 - return appTask.Result;
!
為什么var appTask = RunAsync();
同步阻塞? 這是一個任務...
更新現在我明白了!
你調用 RunAsync:
var appTask = RunAsync();
RunAsync 調用 LoadWithProcessSuddenlyClose:
await LoadWithProcessSuddenlyClose().ConfigureAwait(false);
請注意,這與您編寫的完全相同:
Task t = LoadWithProcessSuddenlyClose();
await t.ConfigureAwait(false);
這意味着您正在從 RunAsync 同步調用 LoadWithProcessSuddenlyClose,並且在它之前沒有異步完成的等待,因此您的 Main 方法已同步調用 LoadWithProcessSuddenlyClose。 然后 LoadWithProcessSuddenlyClose 同步阻塞:
var result = LoadString().Result;
等待調用 ctrl-C 處理程序。 您創建了一個死鎖:您正在阻塞等待僅在此調用返回后才能發生的事情,因為它僅在此調用返回后才被連接,但此調用將永遠不會返回,因為它需要調用已連接的事物這樣做。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.