在控制台应用程序中使用异步和等待

Ros*_*han 5 c# asynchronous async-await

我有四种方法。

  1. Main:只调用preform方法
  2. 工作中:显示“请等待用户”
  3. 花费时间:需要时间执行的程序。
  4. Preform:异步调用取时间和工作方法。

以下是我的代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace AsyncObservation
{
    class Program
    {
        static void Main(string[] args)
        {
            preform();
        }
        public static async Task Working()
        {
            Console.WriteLine("Please wait, the program is running");  
        }
        public static async Task Takingtime()
        {
            Console.WriteLine("This Program started");
            Thread.Sleep(1000);
            Console.WriteLine("The Program finished"); 
        }
        public static async void preform()
        {
            Task timer = Takingtime();
            Task wait = Working();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

最后:我需要显示

This program started. 
Please wait, the program is running 
The program ended.
Run Code Online (Sandbox Code Playgroud)

Har*_*lse 7

我发现你的程序有几个问题。

  • 虽然Preform既不是异步也不是事件处理程序,但它不返回任务
  • Preform在完成 Preform 之前,不会等待开始的任务。因此你永远不知道它们什么时候完成,也不知道结果是什么(例外?)。您甚至可以在程序完成之前结束它们
  • 启动任务后,无法保证它何时运行。如果您等待任务,您只能确定语句已经执行。
  • 使用 async-await 是一种确保您的线程四处查看是否可以做有用的事情的方法,而不是在必须等待某些事情时无所事事地等待。Thread.Sleep是忙碌的等待。如果您想环顾四周看看是否可以做其他事情,请使用await Task.Delay(TimeSpan.FromSeconds(1))。

就您而言,在等待应该写入您的行的过程之前,您无法确定是否已写入任何控制台行。如果您在等待第一个任务之前启动第二个任务,则您不知道第一个任务已经进行了多远,因此您无法确定文本是否已写入控制台。

C# 7.1 引入了 async Task Main(),因此您可以使用它来代替传统的void Main. 它使您无需捕获和解释AggregateException您开始使流程异步的任务所引发的问题。

如果您不想使用async Main,您当然可以使用Task.Run来调用异步函数:

static void Main(string[] args)
{
    try
    {
        var preformTask = Task.Run( () => Preform() );

        DoSomethingElse();    // if needed
        preformTask.Wait();   // wait for preformTask to finish

        Console.WriteLine("Task completed; press any key to finish");
        Console.ReadKey();
    }
    catch (Exception exc) // inclusive ggregateException if one of your Task fails
    {
        ProcessException(exc)
    }
}

static async Task preform()
{
    // To be certain that the Console Line has been written: await
    await Takingtime();

    // if here, you are certain that the Line has been written,
    // or course you have lost parallel processing
    await Working();
}
Run Code Online (Sandbox Code Playgroud)

为了完整性:其他功能

public static async Task Working()
{
    Console.WriteLine("Please wait, the program is running");

    // either return a completed Task, or await for it (there is a difference!
    await Task.CompletedTask;
    // or:
    return Task.CompletedTask; // do not declare async in this case
}

public static async Task Takingtime()
{
    Console.WriteLine("This Program started");

    //Use Task.Delay instead of Sleep
    await Task.Delay(TimeSpan.FromSeconds(1);   // improved readability
    Console.WriteLine("The Program finished");
}
Run Code Online (Sandbox Code Playgroud)

因为Preform你在等待,所以确信文本已经写好了。但是,您失去了一些并行性。

如果您确实希望同时执行这些过程,则无法确定何时写入文本。如果这很重要,则将应首先运行的部分(写入控制台)与应并行运行的部分(Task.Delay)分开

static async Task preform()
{
    // Do the things that should be done before parallel tasks are run
    await DoThisFirst();

    // start the Tasks that can work parallel: not sure what statements are executed first
    var taskA = DoTaskA();
    var taskB = DoTaskB();

    // if here, you are free to do something else
    // can't be sure about the status of taskA nor taskB
    DoSomethingElse();

    // if you want to do something after you know that the tasks have completed:
    // await the tasks here:
    await Task.When (new Task[] {taskA, taskB});

    // if here, you are certain that all parallel tasks have completed successfully
    // if desired fetch the return values of the Tasks:
    var returnValueA = taskA.Result;
    var returnValueB = taskB.Result;

    // do all async things of which you needed to be certain that both tasks finished
    // for example:
    await ProcessResults(returnValueA, returnValueB);
}
Run Code Online (Sandbox Code Playgroud)


tig*_*ars 2

在控制台应用程序中,可以.Wait()在void Main方法中使用调用。

在某些需要同步的上下文中,.Wait()可能会导致死锁(ASP.NET 有一个同步请求,或者具有 UI 线程的 XAML/WinForms),但这里没有任何同步内容。

static void Main() 
{
     preform.Wait()
}
Run Code Online (Sandbox Code Playgroud)

这将等待异步工作同步完成。您需要该方法同步运行,以便它不会提前返回。如果使用async voidandawait方法会立即返回并退出该方法,在方法await中不起作用Main。

我还建议使用await Task.Delay(1000);而不是Thread.Sleep(1000);因为这是async闲置的规范方式。

对于您的代码示例:

class Program
{
    static void Main(string[] args)
    {
        preform().Wait();

        Console.ReadLine();
    }

    public static async Task Working()
    {
        Console.WriteLine("Please wait, the program is running");
    }

    public static async Task Takingtime()
    {
        Console.WriteLine("This Program started");
        await Task.Delay(1000);
        Console.WriteLine("The Program finished");
    }

    public static Task preform()
    {
        return Task.WhenAll(
            Takingtime(),
            Working());
    }
}
Run Code Online (Sandbox Code Playgroud)