Does await new Task(async () => Actually halt execution until finished?

怎甘沉沦 提交于 2019-12-13 09:15:23

问题


I guess I'm not really understanding the await command in c#. I thought that using await would allow a method to continue processing and would would work in a separate thread and return a value once it completed however, I have the following code:

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
    }
}

However, the foreach() executes, waits for the task to complete, and then only after completion, continues to the next. If I remove the await statement, then it runs the task for every loop iteration simultaneously but the Try/Catch block doesn't throw the exception up the stack. Instead it just stop the service entirely as if it were a service unhandled exception.

How can I either get the new task to run once for each for/each loop without waiting for the previous one to complete, or how can I get the try/catch block to throw the exception up the stack so it can get caught and processed in the method's try/catch block?

Thanks!

FOLLOW-UP:

Would this work and still maintain the Try/Catch stack?

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();
    }
}

回答1:


I guess I'm not really understanding the await command in c#.

That's correct. Do some research before you ask a question. There are many StackOverflow questions, articles, videos, tutorial and books that explain how this works.

I thought that using await would allow a method to continue processing and would would work in a separate thread and return a value once it completed

That is 100% absolutely wrong, so stop thinking all of that right now.

However, the foreach() executes, waits for the task to complete,

An asynchronous wait -- await -- waits for a task to complete. That's why its called "await".

It is asynchronous because if the task is not complete then await returns to the caller and does other work on this thread until the task is complete, at which time the work that follows the await is scheduled to execute.

Make sure you understand that await is an operator on tasks. It is not a calling convention on calls. Any expression of awaitable type can be awaited; don't fall into the trap of believing that await makes a call asynchronous. The called method is already asynchronous; the task that is returned is the thing that is awaited.

How can I either get the new task to run once for each for/each loop without waiting for the previous one to complete

Don't await the task. Await means "I cannot proceed until this task is complete."

If your desired workflow is "create one task per object and asynchronously wait for all of them to finish", that's await Task.WhenAll(tasks). So create a sequence of tasks -- either using your loop to build up a list, or a Select to create a sequence -- and then await all of them at once.

Note that exception handling in a WhenAll situation is a little bit unusual. Read the documentation carefully and understand it before you write the code.




回答2:


I know some of these concepts took me a while to to get my head around as well.

Here is a complete example (C# 7.1) that I hope illustrates answers to the questions given in the original post:

How can I either get the new task to run once for each for/each loop without waiting for the previous one to complete, or how can I get the try/catch block to throw the exception up the stack so it can get caught and processed in the method's try/catch block?

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.");
        }        
    }
}


来源:https://stackoverflow.com/questions/47293580/does-await-new-taskasync-actually-halt-execution-until-finished

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!