繁体   English   中英

C#-了解异步/等待

[英]C# - understanding async/await

有关异步编程的C#文档指出:

  • 对于与CPU绑定的代码,您需要等待通过Task.Run方法在后台线程上启动的操作。

  • await关键字是神奇的地方。 它将控制权交给执行await的方法的调用者,最终允许UI响应或服务具有弹性。

  • 应用await关键字后,它将挂起调用方法,并将控制权交还给其调用者,直到完成等待的任务。

考虑到这一点,我测试了一些CPU绑定的代码(发现比特币区块哈希看起来适当现代且困难),以尝试确切了解应用异步/等待时发生了什么:

namespace AsyncronousSample
{
    using System;
    using System.Linq;
    using System.Security.Cryptography;
    using System.Threading.Tasks;

    internal static class Program
    {
        private static async Task Main(string[] args)
        {
            string result = await HashAsync(Guid.NewGuid().ToByteArray(), 4);

            Console.WriteLine("Calculating hash...");
            Console.WriteLine(result);

            Console.Read();
        }

        private static async Task<string> HashAsync(byte[] data, int difficulty = 1)
        {
            int nonce = default;
            string result = default;

            byte[] GetDataBytesWithNOnce()
            {
                return data
                    .Concat(BitConverter.GetBytes(nonce++))
                    .ToArray();
            }

            byte[] ComputeHash(byte[] bytes)
            {
                using (SHA256 sha = SHA256.Create())
                {
                    return sha.ComputeHash(sha.ComputeHash(bytes));
                }
            }

            string ConvertToHash(byte[] hashBytes)
            {
                return BitConverter
                    .ToString(hashBytes)
                    .Replace("-", string.Empty)
                    .ToLower();
            }

            return await Task.Run(() =>
            {
                do
                {
                    result = ConvertToHash(ComputeHash(GetDataBytesWithNOnce()));
                } while (!result.StartsWith(new string('0', difficulty)));

                return result;
            });
        }
    }
}

好的,对于那些对比特币及其运行方式感兴趣的更精明的人,这不是真正的比特币哈希算法,但是它是SHA256(SHA256(data + nonce)) ,因此对于此示例而言足够困难。

期望与现实

我希望“ Calculating hash...将立即打印到控制台,然后在最终找到哈希时打印结果。

实际上,在找到哈希之前,不会将任何内容打印到控制台。

我的理解或代码哪里出了问题?

因为您是在HashAsync上调用awaitHashAsync控件将HashAsync调用方使用,这种情况就是.NET Framework本身,它调用了Main方法。

我认为最简单的方法是将HashAsync返回的Task分配给变量,但要await Console.WriteLine之后再await

private static async Task Main(string[] args)
{
    Task<string> resultTask = HashAsync(Guid.NewGuid().ToByteArray(), 4);

    Console.WriteLine("Calculating hash...");'

    string result = await resultTask;

    Console.WriteLine(result);

    Console.Read();
}

进行此更改后,一旦调用HashAsync ,它将使用Task.Run将工作推送到后台, Task.Run您返回一个Task以观察工作进度。 但是因为您不await它,所以Main方法将继续执行,并且将显示Calculating hash... 仅当您调用await resultTask控件时,才会将其返回给被称为Main任何人,并且执行将被暂停。

您正在等待HashAsync完全完成。

private static async Task Main(string[] args)
{
   Console.WriteLine("Calculating hash..."); // <-- this before await

   string result = await HashAsync(Guid.NewGuid().ToByteArray(), 4);

   Console.WriteLine(result);

   Console.Read();
}

这是因为您await

我认为理解它的最好方法就是将async代码想象成可以同时运行的代码块,但是一旦您等待它,您就会说:“我现在需要该值,除非得到它,否则它将不会进一步进行”,如果您不等待,则说明您正在处理的任务可能处于未完成状态。

此处: string result = await HashAsync(Guid.NewGuid().ToByteArray(), 4);

您正在挂起调用方方法(在您的情况下为Main),一旦“等待”调用完成,该方法将继续。

打印到控制台的操作是通过相同的调用方方法完成的,因此,一旦HashAsync完成,您将看到“正在计算哈希...”。

当执行线程或UI线程执行以await开始的代码行时,UI线程将自动解除阻塞,并在用户可以与UI交互时等待操作完成,在异步操作完成一段时间后,代码将从头开始就开始执行它。

更新资料

在c#7.1中,main方法也可以是异步的,并且可以通过这种方式使用async await

  class Program
  {
      public static async Task Main(string[] args)
      {
         await Task.Run(async () =>
         {
            MyAsyncFunc();
         });
         Console.WriteLine("done");
         Console.ReadLine();
      }

    static async Task MyAsyncFunc()
     {
        await Task.Delay(3000);
     }
  }

暂无
暂无

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

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