react different to multiple async calls - c#

Imagine the following scenario :
public async Task DoMultipleWork() {
var uploadTask = UploadAsync(file);
var processingTask = Task.Run( () => DoCpuWork() );
await Task.WhenAll(uploadTask, processingTask);
Console.WriteLine("upload is done");
Console.WirteLine("processing is done");
}
How can I change that code so that it doesn't matter which one ends first, it execute some particular (sync or async) code.
So I fire the both task and when taskA or taskB ends, I just run some code (sync or async) independently of the other.
I think maybe ContinueWith but I'm not sure because it needs an another async method which is not really needed.
EDIT
As suggested by comments on answer, I want to clear that I want to execute different code depending on the task that completes, and get both Console.WriteLine executed as soon as the original task completes.

You want to use Task.WhenAny which returns the first task that completes. You can then tell which task completed by comparing to the original tasks. Before returning you should wait for the other one to complete explicitly (or wait for both with Task.WhenAll):
public async Task DoMultipleWork()
{
var uploadTask = UploadAsync(file);
var processingTask = Task.Run( () => DoCpuWork() );
var completedTask = await Task.WhenAny(uploadTask, processingTask);
Console.WriteLine("upload or processing is done");
if (completedTask == uploadTask)
{
// Upload completed
}
else
{
// Processing completed
}
await Task.WhenAll(uploadTask, processingTask) // Make sure both complete
Console.WriteLine("upload and processing are done");
}

As I describe on my blog, ContinueWith is dangerous unless you explicitly pass a scheduler. You should use await instead of ContinueWith in ~99% of cases (more detail in another blog post).
In your case:
private async Task UploadAsync(string filepath)
{
var result = await fileManager.UploadAsync(filepath);
Console.WriteLine($"Result from uploading file {result}");
}
private async Task ProcessAsync(string filepath, IProgress<T> progress)
{
await Task.Run(() => wordProcessor.Process(filepath, progress));
Console.WriteLine("processing completed");
}
...
await Task.WhenAll(UploadAsync(filepath), ProcessAsync(filepath, processingProgress));

public async Task DoMultipleWork() {
var uploadTask = UploadAsync(file);
var processingTask = Task.Run( () => DoCpuWork() );
uploadTask.ContinueWith((Task t) => Console.WriteLine("YOUR_MESSAGE"), TaskContinuationOptions.OnlyOnRanToCompletion);
processingTask.ContinueWith((Task t) => Console.WriteLine("YOUR_MESSAGE"), TaskContinuationOptions.OnlyOnRanToCompletion);
await Task.WhenAll(new []{uploadTask, processingTask});
}

Related

Execute a Method after completion of any one task in c#

i have two tasks running asynchronously.
var task1=Task.Run(async()=>{method1();});
var task2=Task.Run(async()=>{method1();});
If any one of the task completed,i want to run an another method(eg:method2()).And after completion of second task, i want to run the same method again(i.e.,method2()) .How to do this?
Assuming your methods are awaitable, Maybe you want something like this
await Task.WhenAny(method1,method1); // wait for something to finish
await method2(); // await for method 2
await Task.WhenAll(method1,method1); // run it all again
// or endlessly
while(!theEndofTheUniverse)
{
await Task.WhenAny(method1,method1);
await method2();
} // rinse and repeate
If any one of the task completed,i want to run an another method(eg:method2()).And after completion of second task, i want to run the same method again(i.e.,method2())
Sounds like you have two parallel paths of execution, each composed of two method calls in series. So....
var task1 = Task.Run( async () => { await method1(); await method2(); });
var task2 = Task.Run( async () => { await method1(); await method2(); });
await Task.WhenAll( new Task[] { task1, task2 } );
If I understand you correctly you have a bunch of tasks and if one of them is finshed you want to run another task?
You can use Task.WhenAny(). It accepts an array of tasks and you can await until the first one is finished
var tasks = new[] { Method1(), Method2(), Method3() };
await Task.WhenAny(tasks);
await Method4();
MSDN: https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task.whenany?view=netcore-3.1

Task.WhenAll but process results one by one

Let's say I have a list of Tasks, and I want to run them in parallel. But I don't need all of them to finish to continue, I can move on with just one. The following code waits for all the tasks to finish to move on. Is there a way for me to individually continue with the task that has completed while waiting for the other ones to finish?
List<string>[] x = await Task.WhenAll(new Task<List<string>>[] { task1, task2 })
// When task1 finishes, I want to process the result immediately
// instead of waiting on task2.
You're probably looking for Task.WhenAny.
I've used it for setting off a pile of tasks and then processing each of them as they become ready, but I suppose you could also just wait for one to finish and continue without the loop if you don't care about dealing with the rest.
while(tasks.Count() > 0)
{
var task = await Task.WhenAny(tasks);
tasks.Remove(task);
var taskresult = await task;
// process result
}
If you are using C# 8 and .NET Core you can take advantage of IAsyncEnumerable to hide this complexity from the consuming side.
Just like this:
static async Task Main(string[] args)
{
await foreach (var data in GetData())
{
Console.WriteLine(data);
}
Console.ReadLine();
}
static async IAsyncEnumerable<string> GetData()
{
List<Task<string>> tasks = new List<Task<string>> {GetData1(), GetData3(), GetData2()};
while (tasks.Any())
{
var finishedTask = await Task.WhenAny(tasks);
tasks.Remove(finishedTask);
yield return await finishedTask;
}
}
static async Task<string> GetData1()
{
await Task.Delay(5000);
return "Data1";
}
static async Task<string> GetData2()
{
await Task.Delay(3000);
return "Data2";
}
static async Task<string> GetData3()
{
await Task.Delay(2000);
return "Data3";
}
You can use Task.WhenAny instead.
Example "stolen" from Stephen Cleary's Blog:
var client = new HttpClient();
string results = await await Task.WhenAny(
client.GetStringAsync("http://example.com"),
client.GetStringAsync("http://microsoft.com"));
// results contains the HTML for whichever website responded first.
Responding to comment
You absolutely can keep track of the other tasks:
// supposing you have a list of Tasks in `myTasks`:
while( myTasks.Count > 0 )
{
var finishedTask = await Task.WhenAny(myTasks);
myTasks.Remove(finishedTask);
handleFinishedTask(finishedTask); // assuming this is a method that
// does the work on finished tasks.
}
The only thing you'd have to watch out for is :
The returned task will always end in the RanToCompletion state with its Result set to the first task to complete. This is true even if the first task to complete ended in the Canceled or Faulted state.
Remarks in WhenAny Doks(Emphasis by me)
In case you want to process the results in order of the completion of the tasks, there is the OrderByCompletion extension method that does exactly that in Stephen Cleary's Nito.AsyncEx library, with the signature below:
// Creates a new collection of tasks that complete in order.
public static List<Task<T>> OrderByCompletion<T>(this IEnumerable<Task<T>> #this);
Usage example:
Task<string>[] tasks = new[] { task1, task2, task3, task4 };
foreach (var task in tasks.OrderByCompletion())
{
string result = await task;
// Do something with result
}
If you prefer not having external dependencies, the source code is here.
Based on the answer of Peter Csala, here a extension method for IAsyncEnumerable:
public static async IAsyncEnumerable<T> OrderedByCompletion<T>(this IEnumerable<Task<T>> tasks)
{
List<Task<T>> taskList = new List<Task<T>>(tasks);
while (taskList.Count > 0)
{
var finishedTask = await Task.WhenAny(taskList);
taskList.Remove(finishedTask);
yield return await finishedTask;
}
}

chaining c# async / await tasks, recreating them as they complete

Aesthetic question really.
Given this code (polling unavoidable):
protected override async Task ExecuteAsync(CancellationToken ct)
{
// move the loop here somehow?
await Task.WhenAll(
Task.Run(async () => await this.PollA(ct), ct),
Task.Run(async () => await this.PollB(ct), ct),
Task.Run(async () => await this.PollC(ct), ct))
.ConfigureAwait(false);
}
the polling methods look like this at the moment, each one has a different delay.
private async Task Poll(CancellationToken ct)
{
while (!ct.IsCancellationRequested)
{
await Task.Delay(Math.Max(1000, CONFIGA), ct);
this._logger.StartAction("poll A status");
this._logger.StopAction("poll A status");
}
}
Is there a way to structure a continuation that removes the loop in each of the Poll methods
private async Task Poll(CancellationToken ct)
{
await Task.Delay(Math.Max(1000, CONFIGA), ct);
this._logger.StartAction("poll A status");
this._logger.StopAction("poll A status");
}
This might not even be the right pattern, but it seems better than having three infinite loops.
Task.WhenAny([A,B,C]) =>
// recreate any complete task as soon as it returns
// and await the new "continuation"?
I have an Aesthetic solution, that is probably not advisable to be used since it will probably cause stack overflow eventually.
It maybe demonstrates why the loop is a better option.
I must admit do not really understand your example in a real world context.
In my mind almost all code that executes for a long time will do it in a finite loop, and thus to check for cancellation after each loop iteration sounds like a good idea to me.
Unless you want your code just to run infinitely until the task is canceled, in which case my aesthetic solution will probably cause a stack overflow if left to long, but it was fun none the less coming up with this code.
I created a Extension method:
public static class Extensions
{
public static async Task ContinueWithInfinitly(this Task task, Func<Task> continuationAction, CancellationToken cancellationToken)
{
await task;
if (!cancellationToken.IsCancellationRequested)
{
var newTask = continuationAction.Invoke();
await newTask.ContinueWithInfinitly(continuationAction, cancellationToken);
}
}
}
Which Base on your code will then be called as follows:
await Task.WhenAll(
Task.Run(async () => await this.PollA(ct).ContinueWithInfinitly(() => PollA(ct), ct)),
Task.Run(async () => await this.PollB(ct).ContinueWithInfinitly(() => PollB(ct), ct)),
Task.Run(async () => await this.PollC(ct).ContinueWithInfinitly(() => PollC(ct), ct)))
.ConfigureAwait(false);
Although I dont see the point of wrapping each method again in a Task.Run.
So i can also just be
await Task.WhenAll(
this.PollA(ct).ContinueWithInfinitly(() => PollA(ct), ct),
this.PollB(ct).ContinueWithInfinitly(() => PollB(ct), ct),
this.PollC(ct).ContinueWithInfinitly(() => PollC(ct), ct))
.ConfigureAwait(false);
You can use Task.WhenAny like this:
private async Task<Tuple<string, int>> Poll(string type, int delay, CancellationToken ct) {
await Task.Delay(Math.Max(1000, delay), ct);
Console.WriteLine($"poll {type} status");
// return input arguments back
return Tuple.Create(type, delay);
}
private async Task PollAll(CancellationToken ct) {
var tasks = new []
{
Poll("A", 3000, ct),
Poll("B", 2000, ct),
Poll("C", 1000, ct)
};
while (!ct.IsCancellationRequested) {
var completed = await Task.WhenAny(tasks);
var index = Array.IndexOf(tasks, completed);
// await to throw exceptions if any
await completed;
// replace with new task with the same arguments
tasks[index] = Poll(completed.Result.Item1, completed.Result.Item2, ct);
}
}

how to wait for task.run

I am writing a windows store app and needs some help on Task.Run method. I am calling service to retrieve data from a service; because I want to cancel the task when internet is disconnected I am passing CancellationToken
await Task.Run(async () => await Download1(), cts.Token);
There is another download method which should run after this above task is finished so I am using await. Both the tasks write to same files so I want to make sure that they do not run in parallel.
await Task.Run(async () => await Download2(), cts.Token);
The issue is that above task 2 starts without task 1 is finished and so both task run in parallel. What I am doing wrong? Please advise.
Download1 looks like this:-
public async Task Download1()
{
await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal,
async () =>
{
Status = "Downloading!";
var ListSetupTasks = new List<SetupView>();
foreach (var setup in AllSetupTasks.Setup)
{
ListSetupTasks.Add(new SetupViewModel(setup));
}
IEnumerable<Task> downloadTasksQuery = from setup in ListSetupTasks where setup.TaskType == TaskType.Web select _masterlistrepository.GetTask(setup, false, datetime, branch);
Task[] downloadTasks = downloadTasksQuery.ToArray();
await Task.WhenAll(downloadTasks);
IEnumerable<Task> FinalTasksQuery = from setup in ListSetupTasks where setup.TaskType == TaskType.Web select _masterlistrepository.GetTask(setup, false);
foreach (var task in FinalTasksQuery)
{
await task;
}
});
}
The CancellationToken is used like this:-
async void NetworkChanged(object sender, NetworkConnectionStatusChangedEventArgs e)
{
if (e.Value == false && LoadingData == true)
{
await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal,
async () =>
{
await _alertmessageservice.ShowAsync("", "Network error. Please retry!");
cts.Cancel();
});
}
}
You use the CoreDispatcher.RunAsync which accepts a DispatchedHandler. DispatchedHandler has this signature:
public delegate void DispatchedHandler()
So when you pass in an async lambda it will end up being async void. That makes it impossible to await as there's no task being returned. This means that everything after an await in your lambdas will run on a different thread concurrently.
You shouldn't be passing an async delegate to that method.
As a side note, passing a CancellationToken doesn't enable to automatically stop the operation. It can only stop the operation before it starts or if you're observing the token somewhere inside the operation.
So, to actually use the CancellationToken Download should accept use it which makes the Task.Run redundant:
await Download1Async(cts.Token);
Can you just call them sequentially?
await Task.Run(async () =>
{
await Download1();
await Download2();
});
You might not even need Task.Run, unless you are calling from a UI thread. If you're already on a background thread, then try just:
await Download1();
await Download2();
Of course, your cancellation token should probably be passed into the download function either way.

Serializing async task and async continuation

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

Categories

Resources