如何知道所有异步/等待完成?

时间:2017-02-14 03:41:45

标签: c# asynchronous async-await

我是异步编程的新手,并尝试编写一些测试代码,看看我能用它做些什么。

下面是我的测试控制台应用程序代码,调用异步方法,它会将一些现有的.docx文件发布到我的web api并转换为pdf。

    static void Main(string[] args)
    {
        //async testing - 5 files batch
        Console.WriteLine("Job Start:");

        for (int i = 0; i < 5; i++)
        {
            var filestream = File.Open(@"c:\pdftest\" + i.ToString() +@".docx", FileMode.Open);

            //delegate
            Action act = async () =>
            {
                await test(filestream, i.ToString());
                filestream.Dispose();
            };

            act();
        }

        Console.WriteLine("End.");
        Console.ReadLine();
    }

和方法:

    static async Task<int> test(FileStream fs, string id)
    {
        var content = new StreamContent(fs);
        var client = new HttpClient();

        var startTime = DateTime.Now;

        //post to web api
        var response = await client.PostAsync("http://localhost:50348/PDFConvert/Convert?name=" + id, content);

        var ts = DateTime.Now - startTime;

        Console.WriteLine("Time Cost: " + ts.TotalSeconds.ToString() + " seconds, " + response.Content.ReadAsStringAsync().Result.Replace("\"", ""));

        client.Dispose();
        return 0;

    }

它到目前为止有效,但有一个问题是“结束”。在控制台窗口的“作业开始:”之后打印,然后等待输出。

所以我的问题是:
1.所有异步/等待完成后如何打印“结束”消息? 2.不确定这是否是最佳做法,那么对现有代码的建议是什么?

非常感谢。

3 个答案:

答案 0 :(得分:3)

除非您正在编写事件处理程序,并且强制它成为导致async void委托的Action委托,否则您永远不应该async void

将您的委托更改为Func<Task>,然后您可以执行一系列任务并对其执行WaitAll

static void Main(string[] args)
{
    //async testing - 5 files batch
    Console.WriteLine("Job Start:");

    var tasks = new Task[5];

    for (int i = 0; i < 5; i++)
    {
        var filestream = File.Open(@"c:\pdftest\" + i.ToString() +@".docx", FileMode.Open);

        //Moved i.ToString() out of the delegate to fix a potential bug with variable capture.
        var stringToPrint = i.ToString()

        //delegate
        Func<Task> act = async () =>
        {
            await test(filestream, stringToPrint);
            filestream.Dispose();
        };

        var task = act();
        tasks[i] = task;
    }

    //In a non console program you will likely want to do "await Task.WhenAll(tasks);" instead.
    Task.WaitAll(tasks);

    Console.WriteLine("End.");
    Console.ReadLine();
}

答案 1 :(得分:2)

  

我是异步编程的新手

我建议我的async intro后面跟我的async best practices文章。

不要将async lambdas与Action个委托类型一起使用;这会产生async void方法。 async void方法的一个缺点是,您无法轻易判断它们何时完成。

Async不会与Console应用程序自然地进行网格划分。您可以阻止异步代码上的主线程,但这不是在任何其他应用程序中使用的好模式:

static void Main(string[] args)
{
  Console.WriteLine("Job Start:");

  MainAsync().GetAwaiter().GetResult();

  Console.WriteLine("End.");
  Console.ReadLine();
}

static async Task MainAsync()
{
  for (int i = 0; i < 5; i++)
  {
    using (var filestream = File.Open(@"c:\pdftest\" + i.ToString() +@".docx", FileMode.Open))
      await test(filestream, i.ToString());
  }
}

因评论而更新

由于您希望同时运行任务,因此您应该遵循我的async intro post中的指导并使用Task.WhenAll

static async Task MainAsync()
{
  var tasks = Enumerable.Range(0, 5).Select(TestFilesAsync).ToList();
  await Task.WhenAll(tasks);
}

static async Task TestFilesAsync(int i)
{
  using (var filestream = File.Open(@"c:\pdftest\" + i.ToString() +@".docx", FileMode.Open))
    await test(filestream, i.ToString());
}

答案 2 :(得分:0)

您可以在Linq Select方法中使用异步lambda来迭代任务列表并等待它们完成

   var list = Enumerable.Range(1,5).Select (
          async i => {
                var filestream = File.Open(@"c:\pdftest\" + i.ToString() +@".docx", FileMode.Open);
                await test(filestream, i.ToString());
                filestream.Dispose();
                }
     );
    Task.WaitAll (list.ToArray ());

如果它不是控制台应用程序而是异步上下文,您可以使用WhenAll代替WaitAll