在我的命令行程序中,用户可以通过选择循环执行两个异步函数(选择 1)或两个同步函数(选择 2)。不幸的是,异步函数的执行行为与同步函数完全相同:选择循环卡住,直到任务完成。也许我对await的理解有误,但它不应该确保程序继续执行吗?我现在坚定地希望在任务仍然繁忙时显示“Hello World”输出。我的思维错误在哪里?
await main();
static async Task main()
{
string input="start";
while(input!="exit"){
Console.WriteLine("choose an option:");
Console.WriteLine("1 asynchrone");
Console.WriteLine("2 synchrone");
Console.WriteLine("exit.");
input=Console.ReadLine();
switch(input){
case "1":await startTaskAsync();Console.WriteLine("Hello World");break;
case "2":startTaskSync(); break;
case "exit":input="exit"; break;
default:input="exit";break;
}
}
}
static async Task startTaskAsync(){
var FrogTask=startFrogAsync();
var MouseTask=startMouseAsync();
List<Task> allTask=new List<Task>(){FrogTask,MouseTask};
Task.WaitAll(MouseTask,FrogTask);
Console.WriteLine(FrogTask.Result);
Console.WriteLine(MouseTask.Result);
}
static void startTaskSync(){
string result1=startFrogSync();
string result2=startMouseSync();
Console.WriteLine(result1);
Console.WriteLine(result2);
}
static async Task<string> startMouseAsync(){
string result ="startMouseAsync completed";
Thread.Sleep(2000);
return result;
}
static async Task<string> startFrogAsync(){
string result ="startFrogAsync completed";
Thread.Sleep(10000);
return result;
}
static string startMouseSync(){
string result ="startMouseSync completed";
Thread.Sleep(2000);
return result;
}
static string startFrogSync(){
string result ="startFrogSync completed";
Thread.Sleep(10000);
return result;
}
最佳答案
在写出 Hello world 之前等待任务,这意味着您告诉它在继续执行之前等待该任务完成。
await startTaskAsync();
Console.WriteLine("Hello World");
相反,你应该有这样的东西:
var myTask = startTaskAsync();
Console.WriteLine("Hello World");
await myTask;
另一个问题是在 StartTaskAsync
方法中,您使用 WaitAll
而不是 WhenAll
,这又导致该方法成为同步方法。
而是这样做:
static async Task StartTaskAsync(){
string[] results = await Task.WhenAll(startMouseAsync(),startFrogAsync());
foreach (string result in results)
Console.WriteLine(result);
}
然后我会附议“不要使用Thread.Sleep
”。现在,您的 startMouseAsync
或 startFrogAsync
实际上都不是异步的,大多数 IDE 都会就此警告您。
虽然您可以简单地在这些方法中添加 await Task.Yield();
来强制它们释放线程的控制权,但我会非常不鼓励这样做,即使这样使用 Thread .Sleep
仍然很糟糕,因为它“保留”一个线程而不是释放它,而等待 Task.Delay
将释放线程。
然而事实证明,使用 Thread.Sleep 非常适合演示有关 async/await 的某些事情。如果您知道某个地方需要进行一些繁重的计算工作,则可以使用 Thread.Sleep/Thread.SpinWait 来模拟该行为。
尝试将您的 startFrogAsync
和 startMouseAsync
实现为:
static async Task<string> startMouseAsync()
{
await Task.Yield();
Thread.Sleep(2000);
string result ="startMouseAsync completed";
return result;
}
对比
static async Task<string> startMouseAsync()
{
Thread.Sleep(2000);
await Task.Yield();
string result ="startMouseAsync completed";
return result;
}
在这里您会注意到最后一个似乎也不起作用,这是因为您在释放线程控制之前执行了“繁重的工作”,换句话说,直到 await 任务。 Yield();
,你的代码实际上是同步运行的。
总而言之,上面对Yield的使用只是为了演示一些观点,我个人是不提倡使用yield的。
如果您有一些密集的工作需要卸载到线程池上的单独线程,您可以使用 Task.Run 或类似的方法。
这样:
static Task<string> startMouseAsync()
{
return Task.Run(() => {
string result ="startMouseAsync completed";
//Do your intensive work. Next lines are just some dummy work to illustrate.
for (int i = 0; i < 10000000; i++)
IsPrime(i);
return result;
});
}
bool IsPrime(int number)
{
if (number < 2)
return false;
double possibleFactors = Math.Sqrt(number);
for (int factor = 2; factor <= possibleFactors; factor++)
if (number % factor == 0)
return false;
return true;
}
在代码中使用 async/await 的其他场景包括等待外部资源,例如数据库查询、Web 请求、IO 请求等。
关于c# - C# 中的 Async 和 Await 未正确理解,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/73234228/