1

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();
    }
}
Nick Posey
  • 109
  • 3
  • 9
  • 2
    In this case it is important to know the difference between asynchronous and multi-threaded. [Check out this post.](https://stackoverflow.com/questions/34680985/what-is-the-difference-between-asynchronous-programming-and-multithreading) – burnttoast11 Nov 14 '17 at 19:22
  • 1
    In your follow-up there are no awaits, so there is no obligation that *anything* happens in *any order*. **An await is a point in an asynchronous workflow where the workflow will not continue until the awaited task has completed normally or exceptionally**. If there's a point in your workflow where you need to know if a task has completed normally or exceptionally then there needs to be an `await` there, but there are none in your example. – Eric Lippert Nov 15 '17 at 18:15

2 Answers2

0

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.");
        }        
    }
}
Rob Smith
  • 108
  • 2
  • 7
  • Thanks for that example. My only concern however is that in my case, as I'm understanding it,I need to constantly keep 4 tasks running simultaneously until all tasks are completed. So when task 1 finishes, task 5 should start, when task 2 completes, then task 6, and so on.. Using the WaitAll(), that means that all 4 tasks have to complete before any (or all) of the next 4 tasks run, correct? – Nick Posey Nov 14 '17 at 23:53
  • I think this requirement is probably a different question which you should ask separately. Think about why you believe you have this requirement - is it really a requirement? Otherwise "TPL dataflow" might be what you're looking for. – Rob Smith Nov 15 '17 at 08:18
-2

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.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • I appreciate the feedback and information, and please understand that I have been doing research as I always do before asking a question here. However, in this particular case, I was getting varying results while doing the research. My original problem was that I've read varying articles telling me that a try/catch block won't work properly unless you await the task, and other articles tell me that by awaiting the task, you are making your task more synchronous instead of async. – Nick Posey Nov 14 '17 at 20:57
  • What I'm ultimately trying to accomplish is that my task runs in a separate thread asynchronously multiple times, but in each case, if it fails, it returns the thrown exception to the originator. However, it's not. This is a windows service and once the catch block hits, the service stops.Again, thanks for your information. The original code did NOT await the task that way it would execute the new task for each table. But again, the try/catch is not sending the exception up the stack which I ultimately need. Any suggestions? Thanks again. – Nick Posey Nov 14 '17 at 21:01
  • Do you think something like this would work: var tableTasks = new List { new Task(async () => { try { //Do stuff } catch (DataError e) { throw e; } catch (Exception e) { throw e; } } ) }; Task.WaitAll(tableTasks.ToArray()); – Nick Posey Nov 14 '17 at 21:13