【问题标题】:C# Async, why we need to "hold" the console to get the async result?C# Async,为什么我们需要“持有”控制台来获取异步结果?
【发布时间】:2018-02-16 19:42:59
【问题描述】:

在过去的几天里,我一直在试图弄清楚为什么我的异步方法不起作用以及它是一段非常简单的代码。

public class EntryPoint
{
    static void Main()
    {
        RunTheTaskAsync();
        //Console.ReadLine(); // if I add this to "hold" the finish of the project, I will see the result, without it I dont
    }

    public async static void RunTheTaskAsync()
    {
        Task<string> task = Concatenator('1', 200000);

        Console.WriteLine("Heeeelllooooooo");
        Console.WriteLine("I am running while Concatenator is concatenating in the background.");
        Console.WriteLine("You will receive the results shortly");
        string result = await task;

        Console.WriteLine("Result is: " + result.Length);
    }

    public static Task<string> Concatenator(char characterToConcatenate, int count)
    {
        Console.WriteLine("Concatenating!");

        return Task<string>.Factory.StartNew(() =>
        {
            string concatenatedString = "";

            for (int i = 0; i < count; i++)
            {
                concatenatedString += characterToConcatenate;
            }

            return concatenatedString;
        });
    }
}

如果我按照上面的示例运行它,我永远看不到结果,我只看到 Console.WriteLine 的并按任意键继续.. 之后没有结果。 如果我取消注释 Console.ReadLine();一切都按我的预期工作,我根本不明白为什么!?!如果我添加一些 Thread.Sleep(xxx) 或另一段比“连接器”方法执行时间更长的代码,情况也是如此。 这不是await 应该自行解决的问题之一吗,它的名字就暗示了这一点。 我的理解是:

  • Task 方法开始在后台执行
  • 我们继续使用异步方法中的 3 个 ConsoleWritelines
  • 由于没有其他事情可做,任务还没有完成,我们到达await这一行,我们应该等待任务 完成,然后继续执行其余代码。

只有在中间有另一段代码比 Task 方法需要更长的时间才能执行,或者如果我使用 Console.ReadLine() 并且我找不到关于此的解释时,它才会像这样工作!

我还有一个关于我的实现语法的次要问题。这是创建任务方法的正确方法吗?通过使用

public Task<T> SomeMethodName(some, args)
{
    return Task<T>Factory.StartNew(() => 
    { 
        somecode that returns T;
    }); 
}

【问题讨论】:

  • 您不应该 `await RunTheTaskAsync();` 以防止 Main 在 RunTheTaskAsync 完成之前返回吗?
  • 澄清一下 - 你的问题是“为什么我在操作完成之前结束进程我没有得到操作结果?”
  • RunTheTaskAsync() 也需要返回Task,而不是void
  • 为什么会被否决?这是一个明确、具体的问题,甚至包括一个 mcve。
  • 这里更根本的问题是:您试图从控制台应用程序中了解异步代码是如何工作的。这是个坏主意。编写一个 GUI 应用程序并使其异步; GUI 从一开始就被设计为支持异步工作流,因为它们有一个消息循环。

标签: c# asynchronous async-await


【解决方案1】:

async voids(就像你的 RunTheTaskAsync 方法)是“一劳永逸”。您无法跟踪他们的进度,并且几乎不想使用它们。

相反,创建一个async Task 并等待它:

static async Task Main()
{
    await RunTheTaskAsync();
}

public static async Task RunTheTaskAsync()
{
    ...
}

更多详情见:

注意:2017 Update 3 之前的 Visual Studio 不支持 async Main 方法。在这种情况下,你必须manually await the Task

static void Main()
{
    RunTheTaskAsync().GetAwaiter().Wait();
}

关于您的第二个问题(未来:SO 的政策是“每个问题一个问题”):是的,如果您需要在单独的线程中运行任务,Task.Factory.StartNew 是正确的。较新版本的 .NET 作为提供 Task.Run 作为快捷方式,请参阅以下问题了解详情:

【讨论】:

  • Main 也必须是异步任务才能等待 RunTheTaskAsync。或者在 C# 7 之前不支持异步 main 应该使用 RunTheTaskAsync().GetAwaiter().Wait();
  • @Heinzi 谢谢,我设法让它工作。但我现在有几个新问题。 1. 代码只会在async方法内异步执行,方法外的代码仍会同步运行并等待async方法完成,本例中cw("Haha")不会出现在结果,所以确实是在等待异步方法先完成,截图->prntscr.com/if8b2e我猜这个问题只需要一个真/假答案:D
  • @Darkbound:好吧,调用.Result 会导致您的代码阻塞,直到结果可用。因此,在您真正需要结果才能继续之前,您不应该这样做。正确的方法是:Task&lt;string&gt; t = RunTheTaskAsync(); Console.WriteLine("haha"); Console.WriteLine(t.Result);.
  • @Heinzi 我明白了,所以通过做我正在做的事情,我正在“强制” Main 方法中的代码同步运行,但 async 方法中的代码仍将异步运行
  • @Heinzi prntscr.com/if8mh8 你会说这是我试图做的更好的实现吗?对我来说似乎更干净,并且正在按照我的预期工作。
猜你喜欢
  • 1970-01-01
  • 2017-10-26
  • 2015-10-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多