简体   繁体   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>”?

Please help to understand what's going on.请帮助了解发生了什么。

I have async method.我有async方法。 It return Task<T> .它返回Task<T>

I need wait this method result BUT with correct stop by Ctrl+C.我需要等待此方法结果,但按 Ctrl+C 正确停止。

The problem is:问题是:

If I wait in this way - all works fine :如果我以这种方式等待 -一切正常

      var result = **await Task<T>**

If I wait in this way - main process suddenly stoped :如果我以这种方式等待 -主进程突然停止

      var result = **Task<T>.Result** 

To show it in code, I write small test (sorry - I can't make it smaller)为了在代码中显示它,我写了一个小测试(对不起 - 我不能让它更小)

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;
        }

    } 
}

How to check:如何检查:

If You run the test and press Ctrl+C you will see such output:如果您运行测试并按 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!
%

All works fine!一切正常!

Now in method RunAsync comment LoadWorksFine() and uncomment LoadWithProcessSuddenlyClose() .现在在方法RunAsync注释LoadWorksFine()并取消注释LoadWithProcessSuddenlyClose() Like this:像这样:

    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;
    }

Now run the test and press Ctrl+C you will see such output:现在运行测试并按 Ctrl+C 你会看到这样的输出:

% dotnet StopTest4.dll
RunAsync START
Befor LoadString
LoadString: Start
^C                  <---- Here press Ctrl+C
%

There are no any exceptions or errors - just process stopped.没有任何异常或错误 - 只是进程停止了。

It works on MacOS, Linux and Windows.它适用于 MacOS、Linux 和 Windows。

Who knows why?谁知道为什么? As for me - both variants correct.至于我 - 两种变体都是正确的。

Thank You!谢谢!

PS: I want second variant - I need to call async method in synchronous method. PS:我想要第二个变体 - 我需要在同步方法中调用异步方法。

I post the question on dotnet git.我在 dotnet git 上发布了这个问题。

And got the answer:并得到了答案:

Because your example is synchronously blocking the RunAsync method waiting for the handler to be invoked, but by that point you haven't hooked up the handler.因为您的示例同步阻塞 RunAsync 方法等待调用处理程序,但此时您还没有连接处理程序。 So when ctrl-C is pressed, there isn't a handler to cancel the ctrl-C, and the OS kills the process.因此,当按下 ctrl-C 时,没有取消 ctrl-C 的处理程序,操作系统会终止该进程。

And yes - in this code:是的 - 在这段代码中:

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;
}

line Console.CancelKeyPress += OnCancelKeyPressed;Console.CancelKeyPress += OnCancelKeyPressed; never called.从来没有叫过。

If put it BEFOR var appTask = RunAsync();如果把它放在前var appTask = RunAsync(); all works fine in both variants.在这两种变体中一切正常。

But now I don't understand WHY in second variant Console.CancelKeyPress += OnCancelKeyPressed;但现在我不明白为什么在第二个变体Console.CancelKeyPress += OnCancelKeyPressed; never called.从来没有叫过。

As I understand, var appTask = RunAsync();据我了解, var appTask = RunAsync(); only create Task.只创建任务。

Task waiting here - return appTask.Result;在这里等待的任务 - return appTask.Result; !

WHY var appTask = RunAsync();为什么var appTask = RunAsync(); synchronously blocked?同步阻塞? It is a TASK...这是一个任务...

UPDATE Now I understood!更新现在我明白了!

You call RunAsync:你调用 RunAsync:

var appTask = RunAsync();

RunAsync calls LoadWithProcessSuddenlyClose: RunAsync 调用 LoadWithProcessSuddenlyClose:

await LoadWithProcessSuddenlyClose().ConfigureAwait(false);

Note that this is exactly the same as if you'd written:请注意,这与您编写的完全相同:

Task t = LoadWithProcessSuddenlyClose();
await t.ConfigureAwait(false);

meaning you're calling LoadWithProcessSuddenlyClose synchronously from RunAsync, and there are no awaits that complete asynchronously prior to it, so your Main method has synchronously invoked LoadWithProcessSuddenlyClose.这意味着您正在从 RunAsync 同步调用 LoadWithProcessSuddenlyClose,并且在它之前没有异步完成的等待,因此您的 Main 方法已同步调用 LoadWithProcessSuddenlyClose。 Then LoadWithProcessSuddenlyClose synchronously blocks:然后 LoadWithProcessSuddenlyClose 同步阻塞:

var result = LoadString().Result;

waiting for the ctrl-C handler to be invoked.等待调用 ctrl-C 处理程序。 You've created a deadlock: you're blocking waiting for something that can only happen after this call returns, because it's only hooked up after this call returns, but this call will never return because it requires the hooked up thing to be invoked to do so.您创建了一个死锁:您正在阻塞等待仅在此调用返回后才能发生的事情,因为它仅在此调用返回后才被连接,但此调用将永远不会返回,因为它需要调用已连接的事物这样做。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM