【问题标题】:Does the following code sequence make sense in C#以下代码序列在 C# 中是否有意义
【发布时间】:2018-01-09 20:14:50
【问题描述】:

我没有经常使用 async/await,而且我对它们并不完全满意。我正在尝试进行同步操作以异步运行。你能告诉我下面的代码 sn-p 是否有意义:

public static async Task<string> FileVersionAsync(string localFilePath)
{
    FileVersionInfo versionInfo;
    try
    {
        versionInfo = await Task.FromResult(FileVersionInfo.GetVersionInfo(localFilePath));
    }
    catch (SecurityException)
    {
        return "File Version Checker does not have permission to read file version";
    }
    catch (FileNotFoundException)
    {
        return "Unable to find configured file";
    }
    if (versionInfo.FileVersion == null)
        return "N/A";

    return versionInfo.FileVersion;
}

【问题讨论】:

  • 不确定你想做什么。您编写的代码将与 async / await 一起使用,但不会异步执行。它会阻塞 FileVersionInfo.GetVersionInfo()。如果您希望代码真正异步执行,请将 Task.FromResult 替换为 Task.Run。
  • @Nick 不会使其异步。它会同步执行,但 parallel 在另一个线程上(这可能是 OP 想要的)。
  • 如果存在,最好使用 GetVersionInfo 的异步版本。
  • 在所有情况下都返回一个字符串很糟糕。
  • 代码编译@LarsTech。

标签: c# async-await


【解决方案1】:

添加 Taskawaitasync 不会使某些东西异步;他们只是提供管道来处理异步代码当它发生时。在您的示例中:它永远不会异步发生,因此您所做的只是增加管道开销而没有任何好处。

编译器将生成一个 ton 的额外代码,这些代码将永远不会被命中,因为当它到达 await 时,它会发现任务已经完成,并且会只需继续现有线程即可。

要真正实现异步,您需要......异步的东西。这可能是外部 IO,也可能是线程相关的东西 - 但请注意,简单地跳转到另一个线程不会给您带来任何好处:它只是添加了一个上下文切换。

如果有一个FileVersionInfo.GetVersionInfoAsync 方法,你所做的可能是值得的。

【讨论】:

    【解决方案2】:

    不,这没有意义。

    使你的函数异步的唯一原因是它内部的某个地方等待其他异步函数。事实上,如果您忘记在某处等待,您的编译器会发出警告。

    async-await 语法的发明是为了替代其他任务函数,如 Task.ContinueWith、Task.FromResult、Task.FromException 等。

    In this interview Eric Lippert compared async-await with a cook who has to prepare breakfast 在中间某处搜索 async-await。

    如果厨师必须准备早餐,他就会开始烧水。但他没有等水煮熟,而是开始切面包,做其他事情。等到他无事可做之后,他才开始无所事事地等水烧开,然后泡茶。

    类似地:如果程序必须等待外部进程执行请求,例如数据库查询、将数据写入文件、从 Internet 获取信息等。async-await 确保您的线程不会等待无所事事。取而代之的是,您的线程会向上调用其调用堆栈,以查看其中一个调用者是否可以在没有来自另一个进程的结果的情况下继续工作。

    您将在以下代码中看到这一点:

    public async Task<string> ReadTextFile()
    {
        StreamReader txtReader = File.OpenText(...);
        // read the complete text file; don't wait until finished yet
        Task<String> taskTxt = txtReader.ReadToEndAsync();
    
         // as we didn't use await, we can continue processing:
         DoSomething();
    
         // now we need the result from the read action: await for it
         // the result of await Task<TResult> is the TResult:
         string importedTxt = await taskTxt;
         return importedTxt;
    }
    

    异步等待的一些准则:

    • 只有在调用至少一个其他异步函数时才使用 async-await
    • 而不是void返回Task,而不是TResult返回Task&lt;TResult&gt;
    • 唯一的例外:异步事件处理程序:此异步函数返回 void
    • await Task的返回是voidawait Task&lt;TResult&gt; 的返回是 TResult
    • 如果您现在不需要异步函数的结果,并且如果您可以在任务处理过程中做一些有意义的事情:通过调用异步函数启动任务,但不要等待它。仅在您需要结果时等待

    .

    public async void Button_Clicked(object sender, EventArgs e)
    {
         // indicate you will do something time consuming:
         this.ProgressBar1.Visible = true;
    
         await DoSomethingTimeconsumingAsync(...);
    
         // finished:
         this.progressBar1.Visible = false;
    }
    

    这将确保当您的线程必须等待某事时,它可以执行其他操作,因此您的 GUI 保持响应。

    【讨论】:

      【解决方案3】:

      没有。似乎完全没有必要将此方法设为异步,因为 Task.FromResult 立即准备就绪。

      【讨论】:

      • Task.FromResult 还没有“准备好”立即。它阻塞直到GetVersionInfo返回。然后,是的,Task 已经完成。
      • 嗯,这取决于您所说的立即准备好是什么意思——我的意思是它一被调用就返回(这是立即的合理定义)。当然,只有在GetVersionInfo返回后才会调用。
      • 同意这一点,答案中的措辞在这一点上听起来有点不清楚。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-01-21
      • 2014-04-17
      • 2011-07-16
      相关资源
      最近更新 更多