簡體   English   中英

為什么在 Ctrl+C 我的進程突然停止在“任務<T> .Result”,但適用於“await Task”<T> ”?

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM