Call long running method and continue with other tasks - c#

public class PerformMainTask()
{
Task1();
Task2();
PerformLongTask();
Task3();
Task4();
}
What I would like to achieve here is to PerformLongTask() onto another thread, and to continue with Task3 & Task4 even when PerformLongTask() is still running.
How should my PerformLongTask() be like in a C# 5.0 way?
Do I need to use async/await?

The simplest solution I know of is:
Task1();
Task2();
var task3 = Task.Run(() => PerformLongTask());
Task4();
Task5();
task3.Wait(); //if task3 has not started yet it will be inlined here
Simple and efficient. If you need to propagate errors you should probably use Parallel.Invoke:
Parallel.Invoke(
() => { PerformLongTask(); },
() => { Task4(); Task5(); }
);

Assuming you are using C# 5 and all your Task() methods truly return a Task (or anything awaitable), your code should look like that:
public async Task PerformMainTask()
{
await Task1();
await Task2();
// Start long task
var longTask = PerformLongTask();
await Task3();
await Task4();
//wait for long task to finish
await longTask;
}
However, if your long task does not run parallelly on its own, you can force it to do so with Task.Run:
public async Task PerformMainTask()
{
await Task1();
await Task2();
// Start long task
var longTask = Task.Run(PerformLongTask);
await Task3();
await Task4();
//wait for long task to finish
await longTask;
}
If none are your tasks are really tasks, just strip all the await except the last one, and you will be good to go.

public class PerformMainTask()
{
Task1();
Task2();
Task.WaitAll(Task.Factory.StartNew(() => PerformLongTask()), Task.Factory.StartNew(() => { Task3(); Task4(); }));
}

Related

launch async methods when others finished, in undetermined order

I have many Tasks, that are running asynchronously
Task<bool> task1 = Task.Run<bool>(() =>
{
return this.addGroupStringToDictionary("IfcPolyline");
});
Task<bool> task2 = Task.Run<bool>(() =>
{
return this.addGroupStringToDictionary("IfcPolyLoop");
});
Task<bool> task3 = Task.Run<bool>(() =>
{
return this.addGroupStringToDictionary("IfcAxis2Placement2D");
});
Task<bool> task4 = Task.Run<bool>(() =>
{
return this.addGroupStringToDictionary("IfcAxis2Placement3D");
});
Now, I would like to execute other tasks, as soon as some of them finish.
Let's say I have 3 tasks that need to be executed after that :
task5 needs to be executed when Task1 and Task2 finished.
task6 needs to be executed when Task3 and Task4 finished.
task7 needs to be executed when Task1 and Task6 finished.
How can I do that, cause if I use await Task.WhenAll(task1,task2) before calling task5, I also block execution of task6 and task7 ?
You could use to your advantage the fact that the Task.Run method accepts both synchronous and asynchronous delegates. Using an asynchronous delegate allows you to await previously started tasks:
var task1 = Task.Run(() => { /*...*/ });
var task2 = Task.Run(() => { /*...*/ });
var task3 = Task.Run(() => { /*...*/ });
var task4 = Task.Run(() => { /*...*/ });
var task5 = Task.Run(async () =>
{
await Task.WhenAll(task1, task2);
/*...*/
});
var task6 = Task.Run(async () =>
{
await Task.WhenAll(task3, task4);
/*...*/
});
var task7 = Task.Run(async () =>
{
await Task.WhenAll(task1, task6);
/*...*/
});
For a simple case, you can use Task.ContinueWith(). See Chaining tasks using continuation tasks on Microsoft Learn
For more complex case if you have lots of data to process, you can use DataFlow library to create a pipeline for asynchronous jobs. See How to: Implement a producer-consumer dataflow pattern

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

How to ensure callbacks of parent tasks has been completed in child task

internal int SomeFunction()
{
Task<AddResult> task1 = new Task<AddResult>(() => AddFunction());
task1.Start();
Task<FuncResult> task2 = task1.ContinueWith(task => func1(task1.Result), TaskContinuationOptions.OnlyOnRanToCompletion);
Task task3 = task2.ContinueWith(task => func2(task2.Result), TaskContinuationOptions.OnlyOnRanToCompletion);
Task task4 = task3.ContinueWith(task => func3(task2.Result), TaskContinuationOptions.OnlyOnRanToCompletion);
return 200;
}
void callback(byte response)
{
}
in above func1 and func2 functions will send some data to a device and response will be received in callback function.
func3 will save data into database but before that i need to ensure that all the callbacks are completed. how can i achieve this.
Make use of Task.WhenAll() and store your individual tasks in a collection.
List<Task<t>> _tasks = new List<Task<t>>();
// Now add your tasks...
_tasks.Add(Task<DeviceInfo>(() => AddNodeToNetwork((Modes)mode)));
// Next task, etc.
Finally await for all tasks. This will run the awaiter on the calling thread.
await Task.WhenAll(_tasks);
This will run the awaiter from the a thread pool thread.
await Task.WhenAll(_tasks).ConfigureAwait(false);
I think the trick is to refer to the parameter called 'task' in your lambda expressions. This is the task you're following on from, so if you refer to 'task' rather than 'task1', 'task2', 'task3' etc, then by asking for task.Result, you guarantee that the previous task has been executed.
The only other thing is that you will need to wait for task4 (the final one) to complete, which you could do with a call to Task.WaitAll(new [] { task4 })
internal int SomeFunction()
{
Task<AddNodeResult> task1 = new Task<DeviceInfo>(() => AddNodeToNetwork());
task1.Start();
Task<ZWNode> task2 = task1.ContinueWith(task => GetCommandClassesVersions(task.Result), TaskContinuationOptions.OnlyOnRanToCompletion);
Task task3 = task2.ContinueWith(task => GetManufacturerSpecific(task.Result), TaskContinuationOptions.OnlyOnRanToCompletion);
Task task4 = task3.ContinueWith(task => PersistAddedNode(task.Result), TaskContinuationOptions.OnlyOnRanToCompletion);
return 200;
}
Apologies if this doesn't compile -- just typing in a text editor here!

react different to multiple async calls

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

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