[英]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
上调用await
, HashAsync
控件将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.