问题
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