78

我开始学习 C# 5.0 中的 async / await ,但我完全不明白。我不明白它如何用于并行性。我尝试了以下非常基本的程序:

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

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            Task task1 = Task1();
            Task task2 = Task2();

            Task.WaitAll(task1, task2);

            Debug.WriteLine("Finished main method");
        }

        public static async Task Task1()
        {
            await new Task(() => Thread.Sleep(TimeSpan.FromSeconds(5)));
            Debug.WriteLine("Finished Task1");
        }

        public static async Task Task2()
        {
            await new Task(() => Thread.Sleep(TimeSpan.FromSeconds(10)));
            Debug.WriteLine("Finished Task2");
        }

    }
}

该程序只是阻止调用Task.WaitAll()并且永远不会完成,但我不明白为什么。我确定我只是遗漏了一些简单的东西,或者只是没有正确的心理模型,并且没有任何博客或 MSDN 文章有帮助。

4

5 回答 5

70

我建议您从我的介绍开始async/await并跟进TAP 上的官方 MSDN 文档

正如我在介绍性博客文章中提到的,有几个Task成员是 TPL 的保留者,在纯async代码中没有用处。new Task并且Task.Start应该替换为Task.Run(或TaskFactory.StartNew)。同样,Thread.Sleep应替换为Task.Delay.

最后,我建议你不要使用Task.WaitAll; 您的控制台应用程序应该只Wait在一个Task使用Task.WhenAll. 通过所有这些更改,您的代码将如下所示:

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

    public static async Task MainAsync()
    {
        Task task1 = Task1();
        Task task2 = Task2();

        await Task.WhenAll(task1, task2);

        Debug.WriteLine("Finished main method");
    }

    public static async Task Task1()
    {
        await Task.Delay(5000);
        Debug.WriteLine("Finished Task1");
    }

    public static async Task Task2()
    {
        await Task.Delay(10000);
        Debug.WriteLine("Finished Task2");
    }
}
于 2013-01-06T01:19:35.683 回答
17

了解 C# 任务、异步和等待

C# 任务

任务类是一个异步任务包装器。Thread.Sleep(1000) 可以停止线程运行 1 秒。虽然 Task.Delay(1000) 不会停止当前的工作。见代码:

public static void Main(string[] args){
    TaskTest();
}
private static void TaskTest(){
     Task.Delay(5000);
     System.Console.WriteLine("task done");
}

运行时,“任务完成”将立即显示。所以我可以假设 Task 中的每个方法都应该是异步的。如果我将 TaskTest () 替换为 Task.Run(() =>TaskTest()) 任务完成将不会显示,直到我附加一个 Console.ReadLine(); 在 Run 方法之后。

在内部,Task 类表示状态机中的线程状态。状态机中的每个状态都有多个状态,例如开始、延迟、取消和停止。

异步和等待

现在,您可能想知道如果所有 Task 都是异步的,那么 Task.Delay 的目的是什么?接下来,让我们通过使用 async 和 await 来真正延迟正在运行的线程

public static void Main(string[] args){
     TaskTest();
     System.Console.WriteLine("main thread is not blocked");
     Console.ReadLine();
}
private static async void TaskTest(){
     await Task.Delay(5000);
     System.Console.WriteLine("task done");
}

async 告诉调用者,我是异步方法,不要等我。TaskTest() 中的 await 请求等待异步任务。现在,运行后,程序将等待 5 秒以显示任务完成文本。

取消任务

由于 Task 是一个状态机,因此必须有一种方法可以在任务运行时取消任务。

static CancellationTokenSource tokenSource = new CancellationTokenSource();
public static void Main(string[] args){
    TaskTest();
    System.Console.WriteLine("main thread is not blocked");
    var input=Console.ReadLine();
    if(input=="stop"){
          tokenSource.Cancel();
          System.Console.WriteLine("task stopped");
     }
     Console.ReadLine();
}
private static async void TaskTest(){
     try{
          await Task.Delay(5000,tokenSource.Token);
     }catch(TaskCanceledException e){
          //cancel task will throw out a exception, just catch it, do nothing.
     }
     System.Console.WriteLine("task done");
}

现在,当程序运行时,您可以输入“stop”来取消延迟任务。

于 2016-09-08T03:37:22.983 回答
12

你的任务永远不会完成,因为它们永远不会开始运行。

我会Task.Factory.StartNew创建一个任务并启动它。

public static async Task Task1()
{
  await Task.Factory.StartNew(() => Thread.Sleep(TimeSpan.FromSeconds(5)));
  Debug.WriteLine("Finished Task1");
}

public static async Task Task2()
{
  await Task.Factory.StartNew(() => Thread.Sleep(TimeSpan.FromSeconds(10)));
  Debug.WriteLine("Finished Task2");
}

作为旁注,如果您真的只是想暂停异步方法,则无需阻塞整个线程,只需使用 Task.Delay

public static async Task Task1()
{
  await Task.Delay(TimeSpan.FromSeconds(5));
  Debug.WriteLine("Finished Task1");
}

public static async Task Task2()
{
  await Task.Delay(TimeSpan.FromSeconds(10));
  Debug.WriteLine("Finished Task2");
}
于 2013-01-06T00:21:32.953 回答
8

Async 和 await 是标记代码位置的标记,在任务(线程)完成后控制应该从哪里恢复。这是一个详细的 youtube 视频,它以示范的方式解释了这个概念 http://www.youtube.com/watch?v=V2sMXJnDEjM

如果你愿意,你也可以阅读这篇 coodeproject 文章,它以更直观的方式解释了相同的内容。 http://www.codeproject.com/Articles/599756/Five-Great-NET-Framework-4-5-Features#Feature1:- “异步”和“等待”(代码标记)

于 2013-09-23T18:51:47.250 回答
0
static void Main(string[] args)
{
    if (Thread.CurrentThread.Name == null)
        Thread.CurrentThread.Name = "Main";
    Console.WriteLine(Thread.CurrentThread.Name + "1");

    TaskTest();

    Console.WriteLine(Thread.CurrentThread.Name + "2");
    Console.ReadLine();
}


private async static void TaskTest()
{
    Console.WriteLine(Thread.CurrentThread.Name + "3");

    await Task.Delay(2000);
    if (Thread.CurrentThread.Name == null)
        Thread.CurrentThread.Name = "FirstTask";
    Console.WriteLine(Thread.CurrentThread.Name + "4");

    await Task.Delay(2000);
    if (Thread.CurrentThread.Name == null)
        Thread.CurrentThread.Name = "SecondTask";
    Console.WriteLine(Thread.CurrentThread.Name + "5");
}

如果你运行这个程序,你会看到await它将使用不同的线程。输出:

Main1
Main3
Main2
FirstTask4 // 2 seconds delay
SecondTask5 // 4 seconds delay

但是,如果我们删除这两个await关键字,您会发现async仅此一项并没有多大作用。输出:

Main1
Main3
Main4
Main5
Main2
于 2021-08-03T19:37:15.310 回答