Waiting Until the Task Finishes

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

Waiting until the task finishes

Use DispatchGroups to achieve this. You can either get notified when the group's enter() and leave() calls are balanced:

func myFunction() {
var a: Int?

let group = DispatchGroup()
group.enter()

DispatchQueue.main.async {
a = 1
group.leave()
}

// does not wait. But the code in notify() gets run
// after enter() and leave() calls are balanced

group.notify(queue: .main) {
print(a)
}
}

or you can wait:

func myFunction() {
var a: Int?

let group = DispatchGroup()
group.enter()

// avoid deadlocks by not using .main queue here
DispatchQueue.global(attributes: .qosDefault).async {
a = 1
group.leave()
}

// wait ...
group.wait()

print(a) // you could also `return a` here
}

Note: group.wait() blocks the current queue (probably the main queue in your case), so you have to dispatch.async on another queue (like in the above sample code) to avoid a deadlock.

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.

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 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 make my program wait until a task is finished before continuing?

I required some similar concept in my project. I had to use a inbuilt firebase listener to get the URL.

I used an getDownloadUrl().addOnSuccessListener(), this returned an URI

Here's a part of my code snippet :

filepath.getDownloadUrl().addOnSuccessListener(new OnSuccessListener<Uri>() {
@Override
public void onSuccess(Uri uri) {
Log.d("=== link ",uri.toString());

// Your code
}
});

filepath - it is an StorageReference Object referenced to that file.

Hope this helps.

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

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:

Sample Image

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



Related Topics



Leave a reply



Submit