Task.ContinueWith runs before the first task finishes executing - c#

I have created 3 Tasks. Task3 depend on the result from Task1 and Task2.
While debugging the code it executes correctly but while running the application Task3 gets execute before Task1 and Task2 complete.
Sample code :
enter code here
public void LoadData()
{
// Set up tasks
Task Task1 = GetTask1Data();
Task Task2 = GetTask2Data();
Task Task3 = GetTask3Data();
new TaskFactory(TaskScheduler).StartNew(() =>
{
// Start first
Task1.Start();
new TaskFactory(TaskScheduler).ContinueWhenAll(
new[] {Task1 },
completedTasks =>
{
Task2.Start();
Task2.ContinueWith(r => Task3.Start());
});
}
}
}
private Task GetTask3Data()
{
return new Task(() =>
{
Task<ICollection<Object>> task = SubTask1();
if (task == null)
{
return;
}
task.ContinueWith(result =>
{
if (result != null)
Debug.WriteLine("Got correct data");
});
});
}
private Task<ICollection<Object>> SubTask1()
{
return new TaskFactory(TaskScheduler).StartNew(() =>
{
if (<bool condition return by Task1> && <book condition return by Task2>)
{
//Return executed data
}
return null;
});
}
Thanks in advance.

It looks like you might be using some kind of global state to hold the results of Task1 and Task2. Try to compose your tasks into a flow.
var task1Result = await GetTask1();
var task2Result = await GetTask2();
var task3Result = await GetTask3(task1Result, task2Result);

Related

How to make one task run after a collection of tasks has finished in C#?

I have a collection of tasks that have to finish before one last task is run. Specifically:
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
var task1 = Task.Run(() =>
{
Thread.Sleep(5000);
Console.WriteLine("Task1");
});
var task2 = Task.Run(() =>
{
Thread.Sleep(5000);
Console.WriteLine("Task2");
});
var task3 = Task.Run(() =>
{
Thread.Sleep(5000);
Console.WriteLine("Task2");
});
var last = Task.Run(() =>
{
Console.WriteLine("Last");
});
var tasks = new List<Task>();
tasks.Add(task1);
tasks.Add(task2);
tasks.Add(task3);
await Task.WhenAll(tasks).ContinueWith(t => last);
}
Currently they finish like this:
Last
Task2
Task3
Last1
I want them to finish like this:
Task1
Task2
Task3
Last
The order of first 3 does not matter, what matters is that Last task finishes last.
I cannot block the thread or wait for the collection to finish and similar stuff, only the Last task has to perform after first three are done.
Tasks are created "hot", i.e., already in-progress. If you want to delay the starting of a task, then use a delegate (Func<Task>) or a separate async method.
I like local async methods for this kind of behavior:
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
async Task Task1() => Task.Run(() =>
{
Thread.Sleep(5000);
Console.WriteLine("Task1");
});
async Task Task2() => Task.Run(() =>
{
Thread.Sleep(5000);
Console.WriteLine("Task2");
});
async Task Task3() => Task.Run(() =>
{
Thread.Sleep(5000);
Console.WriteLine("Task2");
});
async Task Last() => Task.Run(() =>
{
Console.WriteLine("Last");
});
var tasks = new List<Task>();
tasks.Add(Task1());
tasks.Add(Task2());
tasks.Add(Task3());
await Task.WhenAll(tasks);
await Last();
}
P.S. Don't use ContinueWith.
//var last = Task.Run(() =>
//{
// Console.WriteLine("Last");
//});
var tasks = new List<Task>();
tasks.Add(task1);
tasks.Add(task2);
tasks.Add(task3);
await Task.WhenAll(tasks); //.ContinueWith(t => last);
Task.Run(() =>
{
Console.WriteLine("Last");
});

c# async update http then save to local db WhenAll ContinueWith timing

I've got the following code which seems to run fine apart from the continuation on the WhenAll ... await Task.WhenAll(syncTasks).ContinueWith ... is run before all four methods are completed. Would appreciate any guidance on what I'm doing wrong here. I don't really feel like I understand how to arrange complex async functionality and what seems to be happening supports that. This is in a Xamarin App BTW although I don't suppose that really matters.
private async Task SyncItems()
{
var updateItemOnes = Task.Run(() =>
{
UpdateItemOnesToServer(itemOnesToUpdate).ContinueWith(async (result) => {
if (!result.IsFaulted && !result.IsCanceled)
{
await UpdateItemOnesToLocal(itemOnesToUpdate);
}
});
});
syncTasks.Add(updateItemOnes);
var updateItemTwos = Task.Run(() =>
{
UpdateItemTwosToServer(itemTwosToUpdate).ContinueWith(async (result) => {
if (!result.IsFaulted && !result.IsCanceled)
{
await UpdateItemTwosToLocal(itemTwosToUpdate);
}
});
});
syncTasks.Add(updateItemTwos );
//Show Loading Dialog
await Task.WhenAll(syncTasks).ContinueWith((result) => {
if (!result.IsFaulted && !result.IsCanceled)
{
//Success
}
else
{
//Error
}
//Hide Loading Dialog
});
}
private async Task UpdateItemOnesToServer(IEnumerable<Item> itemOnesToUpdate)
{
try
{
var listofTasks = new List<Task>();
foreach (var item in itemOnesToUpdate)
{
var convertItemOneTask = Task.Run(async () => {
//Convert Image File in Item to Base64 here
});
listofTasks.Add(convertItemOneTask);
}
await Task.WhenAll(listofTasks);
var response = await _apiManager.SaveItemOnes(itemOnesToUpdate);
if (response.IsSuccessStatusCode)
{
//Update ItemOnes for Local Update with Response Values
}
}
catch
{
throw;
}
}
private async Task UpdateItemOnesToLocal(IEnumerable<Item> itemOnesToUpdate)
{
var listOfTasks = new List<Task<bool>>();
foreach (var itemOne in itemOnesToUpdate)
{
listOfTasks.Add(_localService.UpdateItemOne(itemOne));
}
await Task.WhenAll<bool>(listOfTasks);
}
private async Task UpdateItemTwosToServer(IEnumerable<ItemOne> itemTwosToUpdate)
{
try
{
var listofTasks = new List<Task>();
foreach (var item in itemTwosToUpdate)
{
var convertItemTwoTask = Task.Run(async () => {
//Convert Image File in Item to Base64 here
});
listofTasks.Add(convertItemTwoTask);
}
await Task.WhenAll(listofTasks);
var response = await _apiManager.SaveItemTwos(itemTwosToUpdate);
if (response.IsSuccessStatusCode)
{
//Update ItemTwos for Local Update with Response Values
}
}
catch
{
throw;
}
}
private async Task UpdateItemTwosToLocal(IEnumerable<ItemTwo> itemTwosToUpdate)
{
var listOfTasks = new List<Task<bool>>();
foreach (var itemTwo in itemTwosToUpdate)
{
listOfTasks.Add(_localService.UpdateItemTwo(itemTwo));
}
await Task.WhenAll<bool>(listOfTasks);
}
Thanks in advance to anyone who can provide a little clarity. It will be much appreciated.
So there are a few problems with this code.
someTask.ContinueWith(X)
Basically this says "once the someTask is completed, do X" (there's more going on but in this case think of it like this). However if you await someTask this will not include the ContinueWith part. So like this the Task.WhenAll(syncTasks) will not wait on your ContinueWith parts.
var updateItemOnes = Task.Run(() => UpdateItemOnesToServer()) wrappers. There is no awaiting here, so this will create a Task that just starts the UpdateItemOnesToServer task. That is done instantly.
If you would like to see what is happening in practice use this test class:
class TestAsyncClass
{
public async Task Run()
{
var tasks = new List<Task>();
Console.WriteLine("starting tasks");
var task1 = Task.Run(() => {
FakeServerCall1().ContinueWith(async (result) =>
{
if (!result.IsFaulted && !result.IsCanceled)
await FakeLocalCall1();
});
});
tasks.Add(task1);
var task2 = Task.Run(() => {
FakeServerCall2().ContinueWith(async (result) =>
{
if (result.IsCompletedSuccessfully)
await FakeLocalCall2();
});
});
tasks.Add(task2);
Console.WriteLine("starting tasks completed");
await Task.WhenAll(tasks);
Console.WriteLine("tasks completed");
}
public async Task<bool> FakeServerCall1()
{
Console.WriteLine("Server1 started");
await Task.Delay(3000);
Console.WriteLine("Server1 completed");
return true;
}
public async Task<bool> FakeServerCall2()
{
Console.WriteLine("Server2 started");
await Task.Delay(2000);
Console.WriteLine("Server2 completed");
return true;
}
public async Task<bool> FakeLocalCall1()
{
Console.WriteLine("Local1 started");
await Task.Delay(1500);
Console.WriteLine("Local1 completed");
return true;
}
public async Task<bool> FakeLocalCall2()
{
Console.WriteLine("Local2 started");
await Task.Delay(2000);
Console.WriteLine("Local2 completed");
return true;
}
}
You'll see that the output is as follows:
starting tasks
starting tasks completed
Server1 started
Server2 started
tasks completed
Server2 completed
Local2 started
Server1 completed
Local1 started
Local2 completed
Local1 completed
Notice here the "tasks completed" is called straight after starting the two tasks.
Now if we change the Run method like this I think we'll get the functionality you're looking for:
public async Task Run()
{
var tasks = new List<Task>();
Console.WriteLine("starting tasks");
var task1 = Task.Run(async () =>
{
await FakeServerCall1();
await FakeLocalCall1();
});
tasks.Add(task1);
var task2 = Task.Run(async() =>
{
await FakeServerCall2();
await FakeLocalCall2();
});
tasks.Add(task2);
Console.WriteLine("starting tasks completed");
await Task.WhenAll(tasks);
Console.WriteLine("tasks completed");
}
Which will output:
starting tasks
starting tasks completed
Server1 started
Server2 started
Server2 completed
Local2 started
Server1 completed
Local1 started
Local2 completed
Local1 completed
tasks completed
So we see here that Local1 is always after Server1 and Local2 is always after Server2 and "tasks completed" is always after Local1 & Local2
Hope this helps!
Edit:
From you comment you said you would like to see any exceptions that occurred in the process. This is where you could use ContinueWith (it is also fired when exceptions are throw:
await Task.WhenAll(tasks).ContinueWith((result) =>
{
if (result.IsFaulted)
{
foreach (var e in result.Exception.InnerExceptions)
{
Console.WriteLine(e);
}
}
});
If you change the following test calls:
public async Task<bool> FakeServerCall2()
{
Console.WriteLine("Server2 started");
await Task.Delay(1000);
Console.WriteLine("Crashing Server2");
throw new Exception("Oops server 2 crashed");
}
public async Task<bool> FakeLocalCall1()
{
Console.WriteLine("Local1 started");
await Task.Delay(1500);
Console.WriteLine("crashing local1");
throw new Exception("Oh ohh, local1 crashed");
}
This will be your output:
starting tasks
starting tasks completed
Server1 started
Server2 started
Crashing Server2
Server1 completed
Local1 started
crashing local1
System.Exception: Oh ohh, local1 crashed
at TestConsoleApp.TestAsyncClass.FakeLocalCall1() in ~\TestConsoleApp\TestConsoleApp\Program.cs:line 67
at TestConsoleApp.TestAsyncClass.b__0_0() in ~\TestConsoleApp\TestConsoleApp\Program.cs:line 17
System.Exception: Oops server 2 crashed
at TestConsoleApp.TestAsyncClass.FakeServerCall2() in ~\TestConsoleApp\TestConsoleApp\Program.cs:line 59
at TestConsoleApp.TestAsyncClass.b__0_1() in ~\TestConsoleApp\TestConsoleApp\Program.cs:line 23
tasks completed

Awaiting Multiple Tasks

I have the following pieces of code:
private async void buttonStart_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e)
{
Bot b = new Bot(_names);
var result = await b.Start(false, true, false);
MessageBox.Show("Done");
}
public async Task<bool> Start(bool instagram, bool twitter, bool xbox)
{
if (twitter)
{
Twitter t = new Twitter(Names);
await t.CheckNames();
}
return true;
}
public Task CheckNames()
{
List<Task> tasks = new List<Task>();
foreach (Name name in Names)
{
tasks.Add(Task.Factory.StartNew(async () =>
{
TwitterResponse result = await Check(name);
MessageBox.Show(result.msg);
}));
}
return Task.WhenAll(tasks);
}
public async Task<TwitterResponse> Check(Name name)
{
HttpClient http = new HttpClient();
HttpResponseMessage response = await http.GetAsync(string.Format("https://twitter.com/users/username_available?username={0}", name.Value));
string html = response.Content.ReadAsStringAsync().Result;
TwitterResponse result = new JavaScriptSerializer().Deserialize<TwitterResponse>(html);
return result;
}
However, I always seem to get the MessageBox saying "Done" before any of the tasks are completed.
Am I doing something wrong, how can I make sure all of the tasks actually complete before getting the messagebox?
The problem is the line tasks.Add(Task.Factory.StartNew(async () =>, you should almost never be using Task.Factory.StartNew and instead use Task.Run(.
The object that StartNew is returning is a Task<Task> which means it does not wait for the inner task to finish. You must either call .Unwrap() on the output of the StartNew before you add it to the collection or, much much better use Task.Run(
tasks.Add(Task.Run(async () =>
{
TwitterResponse result = await Check(name);
MessageBox.Show(result.msg);
}));
Which has a overload that takes in a Func<Task> and will unwrap the inner task for you.

Stop execution if one of the tasks is faulted

I have a question on TPL tasks.
I have some tasks that are a "Show Stoppers", once one of them is faulted i dont want the method to continue running but give an exception and exit.
I tried using TaskContinuationOptions, something like this:
var res = Task.Factory.ContinueWhenAny(
new[] { task1, task2, task3},
task =>
{
throw task.Exception.Flatten();
},
CancellationToken.None,
TaskContinuationOptions.OnlyOnFaulted,
this.taskScheduler);
var res1 = Task.Factory.ContinueWhenAll(
new[] { task1, task2, task3},
tasks =>
{
// DO SOME CODE
},
CancellationToken.None,
TaskContinuationOptions.NotOnFaulted,
this.taskScheduler);
return Task.WhenAny(res, res1).Unwrap();
But unfortunately there is a limitation filtering on a TaskContinuationOptions when continuing on more that a one task.
What is the solution to this?
You can implement a loop which checks if the tasks are faulted as they finish. If one of them faults, you could throw and exit the method:
List<Task> tasks = new List<Task> {task1, task2, task3}; // Show stopping tasks.
while (tasks.Count > 0)
{
var finishedTask = await Task.WhenAny(tasks);
tasks.Remove(finishedTask);
if (finishedTask.Status == TaskStatus.Faulted)
{
// Throw and exit the method.
}
}
// Continuation code goes here.
Note this will not cancel the other ongoing tasks. You could implement a cancelling mechanism if needed using CancellationToken and explicitly cancel the remaining tasks. Your tasks will need to monitor the cancellation token to see if there was a request for cancellation, either by looking at CancellationToken.IsCancellationRequested property or by using the CancellationToken.ThrowIfCancellationRequested method:
var cts = new CancellationTokenSource();
// Generate some tasks for this example.
var task1 = Task.Run(async () => await Task.Delay(1000, cts.Token), cts.Token);
var task2 = Task.Run(async () => await Task.Delay(2000, cts.Token), cts.Token);
var task3 = Task.Run(async () => await Task.Delay(3000, cts.Token), cts.Token);
List<Task> tasks = new List<Task> {task1, task2, task3};
while (tasks.Count > 0)
{
var finishedTask = await Task.WhenAny(tasks);
tasks.Remove(finishedTask);
if (finishedTask.Status == TaskStatus.Faulted)
{
cts.Cancel();
// Throw and exit the method.
}
}
You can use a CancellationTokenSource and run all your tasks with this cancellation token , so if the token is cancelled then rest of tasks will be cancelled as well
var cs = new CancellationTokenSource();
var options = new ParallelOptions { CancellationToken = cs.Token };
var array = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
try
{
Parallel.ForEach(array, options, (index) =>
{
CallCustomMethod(cs, index);
});
}
catch (OperationCanceledException ex)
{
}
void CallCustomMethod(CancellationTokenSource cs, int index)
{
try
{
if (cs.IsCancellationRequested)
{
return;
}
if (index == 4)
{
throw new Exception("Cancel");
}
Console.WriteLine(index);
}
catch
{
cs.Cancel();
}
}

C# task factory timeout

I have to execute a long process operation in a thread and continue by returning the result to a function. Here is my code :
Task<ProductEventArgs>.Factory.StartNew(() =>
{
try
{
// long operation which return new ProductEventArgs with a list of product
}
catch (Exception e)
{
return new ProductEventArgs() { E = e };
}
}).ContinueWith((x) => handleResult(x.Result), TaskScheduler.FromCurrentSynchronizationContext());
The problem is actually I don't have a timeout. I want to put a timer in order to return something like this :
new ProductEventArgs() { E = new Exception("timeout") };
if the timeout is reached.
Can't use await/async.
Thanks a lot !
You should use CancellationTokens:
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10));
var token = cts.Token;
Task<ProductEventArgs>.Factory.StartNew(() =>
{
try
{
// occasionally, execute this line:
token.ThrowIfCancellationRequested();
}
catch (OperationCanceledException)
{
return new ProductEventArgs() { E = new Exception("timeout") };
}
catch (Exception e)
{
return new ProductEventArgs() { E = e };
}
}).ContinueWith((x) => handleResult(x.Result), TaskScheduler.FromCurrentSynchronizationContext());
This code does what you have expressed here:
var timeout = TimeSpan.FromSeconds(5);
var actualTask = new Task<ProductEventArgs>(() =>
{
var longRunningTask = new Task<ProductEventArgs>(() =>
{
try
{
Thread.Sleep(TimeSpan.FromSeconds(10)); // simulates the long running computation
return new ProductEventArgs();
}
catch (Exception e)
{
return new ProductEventArgs() { E = e };
}
}, TaskCreationOptions.LongRunning);
longRunningTask.Start();
if (longRunningTask.Wait(timeout)) return longRunningTask.Result;
return new ProductEventArgs() { E = new Exception("timed out") };
});
actualTask.Start();
actualTask.Wait();
Console.WriteLine("{0}", actualTask.Result.E); // handling E
As you see longRunningTask is created with TaskCreationOptions.LongRunning option. That way it will have a dedicated Thread for it's execution and does not interfere with normal behavior of ThreadPool by occupying a thread from there for too long - which will be needed for other thing like i.e. UI. That's important for long running tasks.
Note: You could then handle actualTask with ContinueWith but I wanted to express the essence here.
You may use returned task object from StartNew method and then use Wait method to determine timeout.
Task<ProductEventArgs> task = Task<ProductEventArgs>.Factory.StartNew(() => {...});
if (!Task.Wait(new TimeSpan(0,0,1,0)) // wait for 1 minute
{
// throw exception or something else if timeout
}
You can run a Task.Delay(timeout) task in parallel and check what task was first to complete (Task.WhenAny() is very handy in this case):
public void FetchProduct(TimeSpan timeout)
{
var fetchTask = Task<ProductEventArgs>.Factory.StartNew(
() =>
{
try
{
// long operation which return new ProductEventArgs with a list of product
}
catch(Exception e)
{
return new ProductEventArgs() { E = e };
}
});
Task<ProductEventArgs> resultTask;
if(timeout != Timeout.InfiniteTimeSpan)
{
var timeoutTask = Task.Delay(timeout);
resultTask = Task.WhenAny(resultTask, timeoutTask).ContinueWith<ProductEventArgs>(
t =>
{
// completed task is the result of WhenAny
if(t.Result == fetchTask)
{
return fetchTask.Result;
}
else
{
return new ProductEventArgs() { E = new TimeoutException() };
}
});
}
else
{
resultTask = fetchTask;
}
resultTask.ContinueWith(x => handleResult(x.Result), TaskScheduler.FromCurrentSynchronizationContext());
}
Note that this solution doesn't have any cancellation logic, and your long running task will be still running even if it times out.
Just start another task within the main task (surrogate):
Task.Factory.StartNew(() =>
{
// returns a string result
var tsk = new Task<string>(() => { return VeryImportantThingsToDo(); });
try
{
tsk.Start();
if (!tsk.Wait(5000))
throw new TimeoutException();
return tsk.Result;
}
catch (TimeoutException)
{
// Jabba Dabba Doooooooohhhhhh
}
return "<unknown>";
}).ContinueWith((o) => string result = o.Result));

Categories