【问题标题】:How to correctly write async method?如何正确编写异步方法?
【发布时间】:2014-01-07 01:44:34
【问题描述】:

所以我正在尝试学习在 C# 中使用“异步”和“等待”的基础知识,但我不确定我在这里做错了什么。我期待以下输出:

Calling DoDownload
DoDownload done
[...output here...]

但我没有得到下载的输出,我也期待“完成”,但这需要一段时间。不应该立即输出吗?另外,我似乎也无法得到字符串结果。这是我的代码:

namespace AsyncTest
{
    class Program
    {
        static void Main(string[] args)
        {
            Debug.WriteLine("Calling DoDownload");
            DoDownloadAsync();
            Debug.WriteLine("DoDownload done");
        }

        private static async void DoDownloadAsync()
        {
            WebClient w = new WebClient();

            string txt = await w.DownloadStringTaskAsync("http://www.google.com/");
            Debug.WriteLine(txt);
        }
    }
}

【问题讨论】:

  • 永远不要写异步 void。您的进程在完成之前退出。您不能在控制台应用程序中执行这样的异步操作。
  • @SLaks, async void 在这种特殊情况下是不合适的(事实上,在大多数情况下)。但是,我仍然反对使用never这个词。在适当的情况下,它是一种有效的工具。

标签: c# async-await c#-5.0


【解决方案1】:

要获得您想要的行为,您需要在退出 Main() 之前等待该过程完成。为了能够判断您的流程何时完成,您需要从您的函数中返回 Task 而不是 void,除非您正在处理事件,否则您永远不应该从 async 函数中返回 void

一个可以正常工作的程序的重写版本将是

class Program
{
    static void Main(string[] args)
    {
        Debug.WriteLine("Calling DoDownload");
        var downloadTask = DoDownloadAsync();
        Debug.WriteLine("DoDownload done");
        downloadTask.Wait(); //Waits for the background task to complete before finishing. 
    }

    private static async Task DoDownloadAsync()
    {
        WebClient w = new WebClient();

        string txt = await w.DownloadStringTaskAsync("http://www.google.com/");
        Debug.WriteLine(txt);
    }
}

因为您不能在Main() 中使用await,所以我不得不改为使用Wait() 函数。如果这是一个具有SynchronizationContext 的应用程序,我会改为使用await downloadTask; 并创建从async 调用的函数。

【讨论】:

  • @PauloMorgado 是的,确实如此,如果您查看 OP 的问题,那就是他想要发生的行为。
  • 这条评论对我有用(“DoDownload done”在结束下载之前打印),但似乎需要一段时间。不应该马上打印出来,然后等待下载吗?
  • 应该是即时的。我建议用您的新代码提出一个新问题,并询问为什么需要这么长时间。
  • @ToddMenier 请不要编辑人们的帖子以进行如此剧烈的更改。在下载完成之前打印“DoDownloadDone”是想要的结果。请参阅此答案的前两个 cmets 以及 OP 帖子中的格式化文本块。
【解决方案2】:

您正在调用 DoDownloadAsync() 但您没有等待它。所以你的程序进入下一行。但是还有另一个问题,异步方法应该返回TaskTask<T>,如果你什么都不返回并且你希望你的方法异步运行,你应该像这样定义你的方法:

private static async Task DoDownloadAsync()
    {
        WebClient w = new WebClient();

        string txt = await w.DownloadStringTaskAsync("http://www.google.com/");
        Debug.WriteLine(txt);
    }

而在Main方法中你不能等待DoDownloadAsync,因为你不能在非异步函数中使用await关键字,你不能使Main异步。所以考虑一下:

var result = DoDownloadAsync();

Debug.WriteLine("DoDownload done");
result.Wait();

【讨论】:

  • 请注意,这几乎总是一个坏主意。
  • @SLaks 为什么这是个坏主意?
  • 是的@SLaks,这个回复错过了它是一个控制台应用程序的上下文。
猜你喜欢
  • 1970-01-01
  • 2013-10-17
  • 2014-12-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多