c# - C# 中的 Async 和 Await 未正确理解

标签 c# async-await .net-6.0

在我的命令行程序中,用户可以通过选择循环执行两个异步函数(选择 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”。现在,您的 startMouseAsyncstartFrogAsync 实际上都不是异步的,大多数 IDE 都会就此警告您。

虽然您可以简单地在这些方法中添加 await Task.Yield(); 来强制它们释放线程的控制权,但我会非常不鼓励这样做,即使这样使用 Thread .Sleep 仍然很糟糕,因为它“保留”一个线程而不是释放它,而等待 Task.Delay 将释放线程。


然而事实证明,使用 Thread.Sleep 非常适合演示有关 async/await 的某些事情。如果您知道某个地方需要进行一些繁重的计算工作,则可以使用 Thread.Sleep/Thread.SpinWait 来模拟该行为。

尝试将您的 startFrogAsyncstartMouseAsync 实现为:

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/

相关文章:

c# - 我是一个初学者,我无法解决C#代码中的一些错误

没有等待或返回的 C# 异步任务方法

c# - MVC Controller 的操作中的异步/等待

c# - 使用 <%= %> 时的不同输出

c# - ListView 上下文菜单上的命令绑定(bind)未触发(未找到)?

c# - .NET6 中具有优先级队列的 Parallel.ForEach

jquery - .Net 6 中的 Asp.net Core 中的 Ajax Post 在 Action 方法中不起作用

c# - 在 .NET Maui 中写入文件

c# - CaSTLe 项目 DynamicProxy 是否大量使用反射?

c# - "The subprocess making the call can not access this object because the owner is another thread"异常异步/等待 WPF C#