Let's say I have two tasks, with the following requirements:
Both are asynchronous.
Both run in parallel
The moment one of them completes I need to know which one did.
I came up with the following code, but it just hangs after both tasks get started (the WaitAny function never returns). I am also getting a squiggly line under Run function telling me to add await inside it, but VS complains when I try to add it in front of Task.WaitAny. Should I be wrapping WaitAny in another Task? What am I doing wrong?
async void Run()
{
Task task1 = Task1();
Task task2 = Task2();
int completedTaskIdx = Task.WaitAny(task1, task2);
Debug.WriteLine("completedTaskIdx = {0}", completedTaskIdx.ToString());
}
async Task Task1()
{
Debug.WriteLine("Task 1 Start");
await Task.Delay(5000);
Debug.WriteLine("Task 1 Stop");
}
async Task Task2()
{
Debug.WriteLine("Task 2 Start");
await Task.Delay(10000);
Debug.WriteLine("Task 2 Stop");
}
Don't block the UI thread when using asnyc/await, you will cause dedlocks. Your WaitAny() causes you to get a deadlock. Use WhenAny instead, you can use Array.IndexOf( to translate the returned task back in to the index.
async Task Run()
{
Task task1 = Task1();
Task task2 = Task2();
var tasks = new[] {task1, task2};
Task completedTask = await Task.WhenAny(tasks);
//It is a good idea to await the retuned task, this is the point a execption would
//be raised if the task finished with a exception.
await completedTask;
int completedTaskIdx = Array.IndexOf(tasks, completedTask);
//.ToString() will cause you to have a bug, you are calling the
//wrong overload of WriteLine. The correct overload will call .ToString() for you.
Debug.WriteLine("completedTaskIdx = {0}", completedTaskIdx);
}
I also fixed a bug in your Debug.WriteLine( call, you where calling this overload when you wanted this overload. I also replaced your async void with async Task, you should never do asnyc void unless you are using it to match a event handler signature.
Related
I created a task like the following code
Task task = new(async () =>
{
// without await Task Delay dont work
await Task.Delay(TimeSpan.FromSeconds(5));
Console.WriteLine("Task is down");
});
task.Start();
var awaiter = task.GetAwaiter();
awaiter.OnCompleted(() =>
{
Console.WriteLine("Task is Completed");
});
Why the task completed first is printed and then the task is down
The task is complete and will be printed if my operation is not finished yet
Because Task constructors are not async aware. No overloads take an function returning a Task so the task created via constructor and async lambda will finish as soon as first await (of unfinished task) is encountered.
And in general you should try avoid using Task constructors and prefer Task.Run instead. From the docs:
This constructor should only be used in advanced scenarios where it is required that the creation and starting of the task is separated.
Rather than calling this constructor, the most common way to instantiate a Task object and launch a task is by calling the static Task.Run(Action) or TaskFactory.StartNew(Action) method.
I have this code:
var task1 = operation1();
var task2 = operation2();
var result1 = await task1;
var result2 = await task2;
I do also handle UnobservedTaskException (by logging it). The problem that I face is that after task1 fails and first await results in exception, task2 completes in an error and then I have a log entry that I do not want to see as I will already log the first exception and at that point all subsequent exceptions are of no interest to me.
So I am wondering if there is a way to do something so that all tasks are "ignored" in a way after I get an exception.
I know I can use await Task.WhenAll, but the downside is that I have to wait for all exceptions to happen, though I know for sure that after first task results in exception, I don't need to wait for the other task to complete as the whole operation already failed.
Another possible solution is to write try/catch and cancel all tasks, but that's a bit cumbersome.
P.S.
The example is simplified, I do have multiple tasks running like that. So I am looking for a generic solution that would work for any number of tasks
As an alternative to Task.WhenAll you can use a progressive approach with Task.WhenAny.
When any of the Tasks finishes then you can examine the result and you can decide what to do next. (Please bear in mind that Task.WhenAny does not throw exception even it is awaited) The great thing about this approach is that you can easily add throttling (control the degree of parallelism) to this.
The basic implementation of progressive async for each
static async Task ProgressiveAsyncForEach(int degreeOfParallelism, params Task[] tasks)
{
var toBeProcessedTasks = new HashSet<Task>();
var remainingTasksEnumerator = tasks.GetEnumerator();
void AddNextTask()
{
if (!remainingTasksEnumerator.MoveNext()) return;
var nextTaskToProcess = (Task)remainingTasksEnumerator.Current;
toBeProcessedTasks.Add(nextTaskToProcess);
}
//Initialize
while (toBeProcessedTasks.Count < degreeOfParallelism)
{
AddNextTask();
}
while (toBeProcessedTasks.Count > 0)
{
var processedTask = await Task.WhenAny(toBeProcessedTasks).ConfigureAwait(false);
if (!processedTask.IsCompletedSuccessfully)
{
Console.WriteLine("One of the task has failed");
//TODO: log first failed task
//CONSIDER: cancel all the remaining tasks
return;
}
toBeProcessedTasks.Remove(processedTask);
AddNextTask();
}
}
Sample usage
static async Task Main(string[] args)
{
await ProgressiveAsyncForEach(2, Faulty(), Fast(), Slow());
Console.WriteLine("Application finished");
}
static async Task Slow()
{
Console.WriteLine("Slow started");
await Task.Delay(1000);
Console.WriteLine("Slow finished");
}
static async Task Fast()
{
Console.WriteLine("Fast started");
await Task.Delay(500);
Console.WriteLine("Fast finished");
}
static async Task Faulty()
{
Console.WriteLine("Faulty started");
await Task.Delay(700);
throw new Exception();
}
Let's assume I have these methods:
public async Task<Something> GetSomethingAsync()
{
var somethingService = new SomethingService();
return await service.GetAsync();
}
and
public Task<Something> GetSomethingAsync()
{
var somethingService = new SomethingService();
return service.GetAsync();
}
Both options compile and work the same way. Is there any best practise as to which option is better of if one is faster then the other?
Or is it just some syntactic sugar?
In the first method compiler will generate "state machine" code around it and execution will be returned to the line return await service.GetAsync(); after task will be completed. Consider example below:
public async Task<Something> GetSomethingAsync()
{
var somethingService = new SomethingService();
// Here execution returns to the caller and returned back only when Task is completed.
Something value = await service.GetAsync();
DoSomething();
return value;
}
The line DoSomething(); will be executed only after service.GetAsync task is completed.
Second approach simply starts execution of service.GetAsync and return correspondent Task to the caller without waiting for completion.
public Task<Something> GetSomethingAsync()
{
var somethingService = new SomethingService();
Task<Something> valueTask = service.GetAsync();
DoSomething();
return valueTask;
}
So in the example above DoSomething() will be executed straight after line Task<Something> valueTask = service.GetAsync(); without waiting for completion of task.
Executing async method on the another thread depend on the method itself.
If method execute IO operation, then another thread will be only waste of the thread, which do nothing, only waiting for response. On my opinion async - await are perfect approach for IO operations.
If method GetAsync contains for example Task.Run then execution goes to the another thread fetched from thread pool.
Below is short example, not a good one, but it show the logic a tried to explain:
static async Task GetAsync()
{
for(int i = 0; i < 10; i++)
{
Console.WriteLine($"Iterate GetAsync: {i}");
await Task.Delay(500);
}
}
static Task GetSomethingAsync() => GetAsync();
static void Main(string[] args)
{
Task gettingSomethingTask = GetSomethingAsync();
Console.WriteLine("GetAsync Task returned");
Console.WriteLine("Start sleeping");
Thread.Sleep(3000);
Console.WriteLine("End sleeping");
Console.WriteLine("Before Task awaiting");
gettingSomethingTask.Wait();
Console.WriteLine("After Task awaited");
Console.ReadLine();
}
And output will be next:
Iterate GetAsync: 0
GetAsync Task returned
Start sleeping
Iterate GetAsync: 1
Iterate GetAsync: 2
Iterate GetAsync: 3
Iterate GetAsync: 4
Iterate GetAsync: 5
End sleeping
Before Task awaiting
Iterate GetAsync: 6
Iterate GetAsync: 7
Iterate GetAsync: 8
Iterate GetAsync: 9
After Task awaited
As you can see executing of GetAsync starts straight after calling it.
If GetSomethingAsync() will be changed to the:
static Task GetSomethingAsync() => new Task(async () => await GetAsync());
Where GetAsync wrapped inside another Task, then GetAsync() will not be executed at all and output will be:
GetAsync Task returned
Start sleeping
End sleeping
Before Task awaiting
After Task awaited
Of course you will need to remove line gettingSomethingTask.Wait();, because then application just wait for task which not even started.
I have long running processing that I want to perform in a background task. At the end of the task, I want to signal that it has completed. So essentially I have two async tasks that I want to run in the background, one after the other.
I am doing this with continuations, but the continuation is starting prior to the initial task completing. The expected behavior is that the continuation run only after the initial task has completed.
Here is some sample code that demonstrates the problem:
// Setup task and continuation
var task = new Task(async () =>
{
DebugLog("TASK Starting");
await Task.Delay(1000); // actual work here
DebugLog("TASK Finishing");
});
task.ContinueWith(async (x) =>
{
DebugLog("CONTINUATION Starting");
await Task.Delay(100); // actual work here
DebugLog("CONTINUATION Ending");
});
task.Start();
The DebugLog function:
static void DebugLog(string s, params object[] args)
{
string tmp = string.Format(s, args);
System.Diagnostics.Debug.WriteLine("{0}: {1}", DateTime.Now.ToString("HH:mm:ss.ffff"), tmp);
}
Expected Output:
TASK Starting
TASK Finishing
CONTINUATION Starting
CONTINUATION Ending
Actual Output:
TASK Starting
CONTINUATION Starting
CONTINUATION Ending
TASK Finishing
Again, my question is why is the continuation starting prior to the completion of the initial task? How do I get the continuation to run only after the completion of the first task?
Workaround #1
I can make the above code work as expected if I make the intial task synchronous - that is if I Wait on the Task.Delay like so:
var task = new Task(() =>
{
DebugLog("TASK Starting");
Task.Delay(1000).Wait(); // Wait instead of await
DebugLog("TASK Finishing");
});
For many reasons, it is bad to use Wait like this. One reason is that it blocks the thread, and this is something I want to avoid.
Workaround #2
If I take the task creation and move it into it's own function, that seems to work as well:
// START task and setup continuation
var task = Test1();
task.ContinueWith(async (x) =>
{
DebugLog("CONTINUATION Starting");
await Task.Delay(100); // actual work here
DebugLog("CONTINUATION Ending");
});
static public async Task Test1()
{
DebugLog("TASK Starting");
await Task.Delay(1000); // actual work here
DebugLog("TASK Finishing");
}
Credit for the above approach goes to this somewhat related (but not duplicate) question: Use an async callback with Task.ContinueWith
Workaround #2 is better than Workaround #1 and is likely the approach I'll take if there is no explanation for why my initial code above does not work.
Your original code is not working because the async lambda is being translated into an async void method underneath, which has no built-in way to notify any other code that it has completed. So, what you're seeing is the difference between async void and async Task. This is one of the reasons that you should avoid async void.
That said, if it's possible to do with your code, use Task.Run instead of the Task constructor and Start; and use await rather than ContinueWith:
var task = Task.Run(async () =>
{
DebugLog("TASK Starting");
await Task.Delay(1000); // actual work here
DebugLog("TASK Finishing");
});
await task;
DebugLog("CONTINUATION Starting");
await Task.Delay(100); // actual work here
DebugLog("CONTINUATION Ending");
Task.Run and await work more naturally with async code.
You shouldn't be using the Task constructor directly to start tasks, especially when starting async tasks. If you want to offload work to be executed in the background use Task.Run instead:
var task = Task.Run(async () =>
{
DebugLog("TASK Starting");
await Task.Delay(1000); // actual work here
DebugLog("TASK Finishing");
});
About the continuation, it would be better to just append it's logic to the end of the lambda expression. But if you're adamant on using ContinueWith you need to use Unwrap to get the actual async task and store the it so you could handle exceptions:
task = task.ContinueWith(async (x) =>
{
DebugLog("CONTINUATION Starting");
await Task.Delay(100); // actual work here
DebugLog("CONTINUATION Ending");
}).Unwrap();
try
{
await task;
}
catch
{
// handle exceptions
}
Change your code to
// Setup task and continuation
var t1 = new Task(() =>
{
Console.WriteLine("TASK Starting");
Task.Delay(1000).Wait(); // actual work here
Console.WriteLine("TASK Finishing");
});
var t2 = t1.ContinueWith((x) =>
{
Console.WriteLine("C1");
Task.Delay(100).Wait(); // actual work here
Console.WriteLine("C2");
});
t1.Start();
// Exception will be swallow without the line below
await Task.WhenAll(t1, t2);
I was just experimenting to see what happens when a cold task (i.e. a Task which hasn't been started) is awaited. To my surprise the code just hung forever and "Finsihed" is never printed. I would expect that an exception is thrown.
public async Task Test1()
{
var task = new Task(() => Thread.Sleep(1000));
//task.Start();
await task;
}
void Main()
{
Test1().Wait();
Console.WriteLine("Finished");
}
Then I though perhaps the task can be started from another thread, so I changed the code to:
public async Task Test1()
{
var task = new Task(() => Thread.Sleep(1000));
//task.Start();
await task;
Console.WriteLine("Test1 Finished");
}
void Main()
{
var task1 = Test1();
Task.Run(() =>
{
Task.Delay(5000);
task1.Start();
});
task1.Wait();
Console.WriteLine("Finished");
}
But it is still blocked at task1.Wait(). Does anyone know if there is way to start a cold task after it has being awaited?
Otherwise it seems there is no point in being able to await a cold task, so perhaps the task should either be started when awaited or an exception should be thrown.
Update
I was awaiting the wrong task, i.e. the outer task returned by Test1 rather than the one newed inside it. The InvalidOperationException mentioned by #Jon Skeet was being thrown inside Task.Run however because the resulting task was not observed, the exception was not thrown on the main thread. Putting a try/catch inside Task.Run or calling Wait() or Result on the task returned by Task.Run threw the exception on the main console thread.
You're trying to start the task returned by the async method - that isn't the cold task that you started out with. Indeed, if you add some diagnostics to your Task.Run call, you'll see that an exception is thrown:
System.InvalidOperationException: Start may not be called on a promise-style task.
Here's an example showing what I think you were really trying to do:
using System;
using System.Threading;
using System.Threading.Tasks;
public class Test
{
static void Main(string[] args)
{
// Not using Task.Delay! That would be pointless
Task t1 = new Task(() => Thread.Sleep(1000));
Task t2 = Await(t1);
Console.WriteLine(t2.Status);
Console.WriteLine("Starting original task");
t1.Start();
Console.WriteLine(t2.Status);
t2.Wait();
Console.WriteLine(t2.Status);
}
static async Task Await(Task task)
{
Console.WriteLine("Beginning awaiting");
await task;
Console.WriteLine("Finished awaiting");
}
}
Note the use of Thread.Sleep instead of Task.Delay; unless you're using the result of Task.Delay, it basically does nothing. Using Thread.Sleep is emulating real work to be done in the other task.
As for why awaiting an unstarted task doesn't throw an exception - I think that's reasonable, to be honest. It allows for situations like the above to be valid, which may in some cases make life easier. (You may create a lot of tasks before starting them, for example - and you may want to start waiting for them to finish before you start them.)
Does anyone know if there is way to start a cold task after it has
being awaited?
You still can create a cold task from an async method and start it later, if that's what you want:
class Program
{
public static async Task Test1()
{
await Task.Delay(1000);
Console.WriteLine("Test1 is about to finish");
}
static void Main(string[] args)
{
var taskOuter = new Task<Task>(Test1);
var taskInner = taskOuter.Unwrap();
Task.Run(() =>
{
Thread.Sleep(2000);
// run synchronously
taskOuter.RunSynchronously();
// or schedule
// taskOuter.Start(TaskScheduler.Defaut);
});
taskInner.Wait();
Console.WriteLine("Enter to exit");
Console.ReadLine();
}
}