How to Wait Until Task Is Finished in C#

How do I wait until Task is finished in C#?

Your Print method likely needs to wait for the continuation to finish (ContinueWith returns a task which you can wait on). Otherwise the second ReadAsStringAsync finishes, the method returns (before result is assigned in the continuation). Same problem exists in your send method. Both need to wait on the continuation to consistently get the results you want. Similar to below

private static string Send(int id)
{
Task<HttpResponseMessage> responseTask = client.GetAsync("aaaaa");
string result = string.Empty;
Task continuation = responseTask.ContinueWith(x => result = Print(x));
continuation.Wait();
return result;
}

private static string Print(Task<HttpResponseMessage> httpTask)
{
Task<string> task = httpTask.Result.Content.ReadAsStringAsync();
string result = string.Empty;
Task continuation = task.ContinueWith(t =>
{
Console.WriteLine("Result: " + t.Result);
result = t.Result;
});
continuation.Wait();
return result;
}

Is there a way to wait for all tasks until a specific result is true, and then cancel the rest?

Here's a solution based on continuation tasks. The idea is to append continuation tasks to each of the original (provided) tasks, and check the result there. If it's a match, the completion source will be set with a result (if there's no match, the result won't be set at all).

Then, the code will wait for whatever happens first: either all the continuation tasks complete, or the task completion result will be set. Either way, we'll be ready to check the result of the task associated with task completion source (that's why we wait for the continuation tasks to complete, not the original tasks) and if it's set, it's pretty much an indication that we have a match (the additional check at the end is a little paranoid, but better safe than sorry I guess... :D)

public static async Task<bool> WhenAnyHasResult<T>(Predicate<T> isExpectedResult, params Task<T>[] tasks)
{
const TaskContinuationOptions continuationTaskFlags = TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.OnlyOnRanToCompletion | TaskContinuationOptions.AttachedToParent;

// Prepare TaskCompletionSource to be set only when one of the provided tasks
// completes with expected result
var tcs = new TaskCompletionSource<T>();

// For every provided task, attach a continuation task that fires
// once the original task was completed
var taskContinuations = tasks.Select(task =>
{
return task.ContinueWith(x =>
{
var taskResult = x.Result;
if (isExpectedResult(taskResult))
{
tcs.SetResult(taskResult);
}
},
continuationTaskFlags);
});

// We either wait for all the continuation tasks to be completed
// (it's most likely an indication that none of the provided tasks completed with the expected result)
// or for the TCS task to complete (which means a failure)
await Task.WhenAny(Task.WhenAll(taskContinuations), tcs.Task);

// If the task from TCS has run to completion, it means the result has been set from
// the continuation task attached to one of the tasks provided in the arguments
var completionTask = tcs.Task;
if (completionTask.IsCompleted)
{
// We will check once more to make sure the result is set as expected
// and return this as our outcome
var tcsResult = completionTask.Result;
return isExpectedResult(tcsResult);
}

// TCS result was never set, which means we did not find a task matching the expected result.
tcs.SetCanceled();
return false;
}

Now, the usage will be as follows:

static async Task ExampleWithBooleans()
{
Console.WriteLine("Example with booleans");

var task1 = SampleTask(3000, true);
var task2 = SampleTask(5000, false);

var finalResult = await TaskUtils.WhenAnyHasResult(result => result == true, task1, task2);

// go ahead and cancel your cancellation token here

Console.WriteLine("Final result: " + finalResult);
Debug.Assert(finalResult == true);
Console.WriteLine();
}

What's nice about putting it into a generic method, is that it works with any type, not only booleans, as a result of the original task.

Waiting for Tasks to finish

I suspect you meant WhenAll rather than WaitAll. WaitAll is a blocking call that returns all the results synchronously, when the original tasks have completed. You can't await that result, as it's not a task.

WhenAll is an asynchronous call that returns a task which completes when all the original tasks have completed. You can await that.

If you change the end of your Run method to:

await Task.WhenAll(tasks);
Console.WriteLine("All tasks completed");

... you'll see the tasks finish and then the completion message.

async & await - How to wait until all Tasks are done?

you need to await the ending of the inner tasks:

static async Task PrintAsync()
{
await Task.WhenAll(Task.Run(() => PrintOne()), Task.Run(() => PrintTwo()));
}

explanation: async Task denotes an awaitable method. Inside this method you can also await Tasks. If you don't do this then it will simply let the tasks loose which will run on their own. Task.Run returns a Task which can be awaited. If you want both tasks to run in parallel you can use the tasks from the retur values and use them in the awaitable method Task.WhenAll

EDIT: Actually Visual Studio would mark this code with a green curvy line. When hoovering with the mouse over it you get a warning:

enter image description here

CS4014

This should explain why "ending" is printed before the tasks have finished

EDIT 2:

If you have a collection of parameters that you want to iterate and call an async method to pass the parameter in, you can also do it with a select statement in 1 line:

static async Task DatabaseCallsAsync()
{
// List of input parameters
List<int> inputParameters = new List<int> {1,2,3,4,5};
await Task.WhenAll(inputParameters.Select(x => DatabaseCallAsync($"Task {x}")));
}

static async Task DatabaseCallAsync(string taskName)
{
Console.WriteLine($"{taskName}: start");
await Task.Delay(3000);
Console.WriteLine($"{taskName}: finish");
}

The last part is similar to a previous answer

How to wait for a thread is finished before execute next code?

I assume this code is trying to run a loop that can be cancelled in the background.
The easy way to do this is by using a CancellationTokenSource and a CancellationToken, eg :

void progr(CancellationToken token)
{
while(!token.IsCancellationRequested)
{
// Do something expensive
}
}

Notice that progr has no reference to external fields. All it needs is that token.

The CancellationTokenSource should be created before the task itself starts.

Task thatTask;
CancellationTokenSource cts;

public void StartTheJob()
{
cts=new CancellationTokenSource();
thatTask=Task.Run(()=>progr(cts.Token));
}

To stop the task, all that's needed is a call to cts.Cancel()

public async Task StopTheJob()
{
cts.Cancel();
//Wait for the cancelled task to exit
await thatTask;
}

To add a delay, use await Task.Delay(...) instead of `Thread.Sleep(). Task.Delay can be cancelled and doesn't block the threadpool thread used to run the task :

async Task progr(CancellationToken token)
{
while(!token.IsCancellationRequested)
{
// Do something expensive
await Task.Delay(1000,token);
}
}

Check Async in 4.5: Enabling Progress and Cancellation in Async APIs for more

How to wait until all the tasks finish before returning from method in C#

When all can be used to create a new task which completes when all tasks in it completes, you can then proceed fetching results out of that task using the regular async patterns.

https://msdn.microsoft.com/en-us/library/system.threading.tasks.task.whenall(v=vs.110).aspx

So
//If your validation results are strings:
Task[] tasks = ...
var things = await Task.WhenAll(tasks);

Alternatively WaitAll can be used, but if you need to do work on the output that is an awkward aproach here. IMO staying in the async context is always better anyways.

Task.WaitAll(tasks);
foreach(var t in tasks) DoSomething(t.Result);

https://msdn.microsoft.com/en-us/library/dd270695(v=vs.110).aspx

Besides, it blocks the current thread AFAIK.


A more complete console example:
https://dotnetfiddle.net/JMLHxR

using System;
using System.Threading.Tasks;

public class Program
{
private static int counter = 0;

public static void Main()
{
Random rnd = new Random();
var tasks = new Task<object>[]
{
// If your target method returns a task.
// - note: A Proxy or similar approach will probably be more readable.
CreateTask(rnd.Next(500,2000)).ContinueWith(t => (object)t.Result),
CreateOtherTask(rnd.Next(500,2000)).ContinueWith(t => (object)t.Result),
CreateTask(rnd.Next(500,2000)).ContinueWith(t => (object)t.Result),
CreateOtherTask(rnd.Next(500,2000)).ContinueWith(t => (object)t.Result),

//If your target method is syncronious.
Task.Run(() => (object)CreateSimple()),
Task.Run(() => (object)CreateMessage()),
Task.Run(() => (object)CreateSimple()),
Task.Run(() => (object)CreateMessage())
};

Task.WaitAll(tasks);
foreach(var t in tasks)
Console.WriteLine(t.Result);

// They are already completed here, but just to show the syntax.
// .Result should obvisously be awaited instead.
var all = Task.WhenAll(tasks).Result;
foreach(var result in all)
Console.WriteLine(result);
}


private static string CreateSimple()
{
int id = Program.counter++;
return "Task [" + id + "] delayed: NONE";
}

private static Message CreateMessage()
{
return new Message(CreateSimple());
}

private static async Task<string> CreateTask(int delay)
{
int id = Program.counter++;
await Task.Delay(delay);
return "Task [" + id + "] delayed: " + delay;
}

private static async Task<Message> CreateOtherTask(int delay)
{
int id = Program.counter++;
await Task.Delay(delay);
return new Message("Task [" + id + "] delayed: " + delay);
}

public class Message {
private string message;

public Message(string msg) { message = msg; }

public override string ToString(){ return message; }
}
}

Waiting for task to be finished

  1. You would have to keep track of the tasks you create to be able to refer to them later. For example:
private static List<Task> _taskList = new List<Task>();

private static void TaskMethod()
{
while(runningService)
{
// This will create more than one task in parallel to run and each task can take upto 30 minutes to finish
_taskList.Add(Task.Run(() => TaskMethod1(arg1)));
}
}

internal static void Stop()
{
runningService = false;
Task.WaitAll(_taskList.ToArray());
Task1.Wait();
}

  1. Because Task1 isn't dependent on the completion of the other tasks. In TaskMethod(), you're just creating the Task and moving on. There is nothing in there that tells it to wait for anything. Unless you await or .Wait() on the Task returned from Task.Run, your code just continues to run with no dependency on the Task you just created.

That's a problem I see in your code as you have it. Your while(runningService) loop will loop as fast as your CPU will allow, creating thousands of new tasks in seconds. Are you sure that's what you want?

Maybe you want it to wait inside the loop for it to complete, before looping and starting a new one? If I'm correct, then your loop should look like this:

private static async Task TaskMethod()
{
while(runningService)
{
// This will create more than one task in parallel to run and each task can take upto 30 minutes to finish
await Task.Run(() => TaskMethod1(arg1));
}
}

But that would only create one Task at a time.

How to wait for all tasks to be completed

There won't be any data in your dict because passing in an async lambda is not going to make any difference in Task's ctor.

You will need to restructure your code quite a bit. You want to call your async method, put the Task in your list and then await till of them complete. Once they're completed, then can you iterate over all your results in your task list and pull out the .Result from it.

var tasks = someDataList.Select(i => _req.ExecuteAsync(i) );
await Task.WhenAll(tasks);
var dict = tasks.ToDictionary(t=> t.Result);
if (dict.Count == List.count()
{
//execute part 2.
}

Note that if _dict is a global variable and you need locking, then you should just replace ToDictionary with your ConcurrentDictionary code above as this is example code.



Related Topics



Leave a reply



Submit