等待新任务(async()=>实际上暂停执行直到完成?

时间:2017-11-14 19:14:24

标签: c# multithreading try-catch

我想我并没有真正了解c#中的await命令。我认为使用await会允许一个方法继续处理,并且可以在一个单独的线程中工作并在完成后返回一个值但是,我有以下代码:

public async Task<DatabaseActionResult> BackupToAzureAsync(DatabaseSchedule schedule)
{
    try
    {       
        foreach (var o in myObjects)
        {
                await new Task(async () =>
                {
                    try
                    {
                        //Do some stuff.
                    }
                    catch (Exception e)
                    {
                        throw; //Should this throw the exception to the functions Try/Catch block so it can log it to the event viewer?
                    }
                }
            }   
        }
    catch (Exception e)
    {
        //Log exception to event viewer and return a DatabaseActionResult object
    }
}

但是,foreach()执行,等待任务完成,然后仅在完成后继续执行下一个任务。如果我删除了await语句,那么它会同时为每个循环迭代运行任务,但Try / Catch块不会将异常抛出堆栈。相反,它只是完全停止服务,就好像它是一个服务未处理的异常。

如何让新任务为每个for / each循环运行一次而不等待前一个循环完成,或者如何让try / catch块将异常抛到堆栈上以便它可以获得在方法的try / catch块中捕获并处理?

谢谢!

后续:

这会工作并仍然保持Try / Catch堆栈吗?

foreach (var t in tables)
{
    try
    {
        var tableTasks = new List<Task>
        {
            new Task(async () =>
                {
                    try
                    {
                        //Do stuff
                    }
                    catch (DataError e)
                    {
                        throw e;
                    }
                    catch (Exception e)
                    {
                        throw e;
                    }
                }
            )
        };
    }
    catch (Exception e)
    {
        return new DatabaseActionResult();
    }
}

2 个答案:

答案 0 :(得分:2)

  

我想我并不是真的了解c#中的await命令。

这是对的。在之前做一些研究你问一个问题。有很多StackOverflow问题,文章,视频,教程和书籍可以解释这是如何工作的。

  

我认为使用await会允许方法继续处理,并且可以在单独的线程中工作并在完成后返回值

这是绝对错误的,所以现在不要再考虑所有这些了。<​​/ p>

  

但是,foreach()执行,等待任务完成,

异步等待 - 等待 - 等待任务完成。这就是为什么它被称为“等待”。

它是异步,因为如果任务未完成,则 await返回给调用者并在此线程上执行其他工作,直到任务完成,此时计划执行await之后的工作。

确保您了解 await是任务的操作员。它不是呼叫上的呼叫约定。任何等待的表达都可以等待;不要陷入相信等待使异步调用的陷阱。被调用的方法已经异步;返回的任务是等待的东西。

  

如何让每个for / each循环运行一次新任务,而无需等待前一个循环完成

不要等待任务。 等待意味着“在此任务完成之前我无法继续。”

如果您所需的工作流程是“为每个对象创建一个任务并异步等待所有对象完成”,那就是await Task.WhenAll(tasks)。因此,创建一系列任务 - 使用循环来构建列表,或者Select来创建序列 - 然后立即等待所有任务。

请注意WhenAll情况下的异常处理有点不寻常。 在编写代码之前,请仔细阅读文档并了解它。

答案 1 :(得分:0)

我知道其中一些概念花了我一些时间才能解决问题。

这是一个完整的例子(C#7.1),我希望能够解释原帖中给出的问题:

  

如何让新任务为每个for / each循环运行一次而不等待前一个循环完成,或者如何让try / catch块将异常抛到堆栈上以便它可以获得在方法的try / catch块中捕获并处理?

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

namespace AsyncTest
{
    class Program
    {
        static async Task Main(string[] args)
        {
            var data = new List<object>() 
            {
                1, 2, "three", 4, 5
            };

            // Transform the data into a sequence of Tasks. Note that this does not start these tasks running.
            IEnumerable<Task> tasks = data.Select(async (o) =>
            {
                Console.WriteLine($"Processing: {o}");
                await Task.Delay(1000);
                if (o is string s) throw new InvalidOperationException($"Cannot perform operation on string '{s}'");
            });

            try 
            {
                Console.WriteLine("Starting asynchronous processing"); 
                // This call starts all the Tasks in the sequence running. If any of those tasks raises an exception
                // the use of the await operator will rethrow that exception here, so we can catch it
                await Task.WhenAll(tasks);
                Console.WriteLine("All tasks processed successfully");
            } 
            catch (InvalidOperationException e) 
            {
                Console.WriteLine($"Error performing asynchronous work: {e.Message}");
                Console.WriteLine("Not all tasks processed successfully");
            }

            Console.WriteLine("Asynchronous processing finished. Exiting.");
        }        
    }
}