How to get notification that a System.Threading.Tasks.Task has completed - c#

I am currently replacing some home baked task functionality with a new implementation using the new System.Threading.Tasks functionality found in .net 4.
I have a slight issue though, and although I can think of some solutions I would like some advice on which is generally the best way to do it, and if I am missing a trick somewhere.
What I need is for an arbitrary process to be able to start a Task but then carry on and not wait for the Task to finish. Not a problem, but when I then need to do something with the result of a task i'm not quite sure the best way of doing it.
All the examples I have seen use either Wait() on the task until it completes or references the Result parameter on the task. Both of these will block the thread which started the Task, which I don't want.
Some solutions I have thought of:
Create a new thread and start the task on that, then use Wait() or .Result to block the new thread and sync the result back to the caller somehow, possibly with polling to the tasks IsCompleted parameter.
Have a 'Notify Completed' task which I can start after completion of the task I want to run which then raises a static event or something.
Pass a delegate into the input of the task and call that to notify that the task is finished.
I can think or pros and cons to all of them, but I especially don't like the idea of having to explicitly create a new thread to start the task on when the one of the aims of using the Task class in the first place is to abstract away from direct Thread usage.
Any thoughts about the best way? Am I missing something simple? Would a 'Completed' event be too much to ask for :)? (Sure there is a good reason why there isn't one!)

I suspect you're looking for Task.ContinueWith (or Task<T>.ContinueWith). These basically say, "When you've finished this task, execute this action." However, there are various options you can specify to take more control over it.
MSDN goes into a lot more detail on this in "How to: Chain Multiple Tasks With Continuations" and "Continuation Tasks".

In modern C#, one no longer needs to call ContinueWith() explicitly. An alternative to the original accepted answer would be to simply create an async method that awaits the Task in question, and does whatever it wants when the Task completes.
For example, suppose you want to raise an event called TaskCompleted when the Task completes. You would write a method like:
async Task RaiseEventWhenTaskCompleted(Task task)
{
await task;
TaskCompleted?.Invoke(this, EventArgs.Empty);
}
To "register" the wait, just call the above method. Add exception handling as desired, either in the method above, or in some code that will eventually observe the Task returned by the above method.

Task task = Task.Run ( () => { Thread.Sleep ( 2000 ); } );
task.GetAwaiter ().OnCompleted ( () =>
{
MessageBox.Show ( "the task completed in the main thread", "");
} );

You can apply a task continuation.
Alternatively, Task implements IAsyncResult, so you can use the standard approaches for that interface (blocking, polling, or waiting on its WaitHandle).

I created a small example illustrating Jon Skeet's answer, which I'd like to share with you:
using System;
using System.Threading;
using System.Threading.Tasks;
public class Program
{
static void Main(string[] args)
{
for (int cnt = 0; cnt < NumTasks; cnt++)
{
var task = new Task<int>(DoSomething); // any other type than int is possible
task.ContinueWith(t => Console.WriteLine($"Waited for {t.Result} milliseconds."));
task.Start(); // fire and forget
}
PlayMelodyWhileTasksAreRunning();
}
static int NumTasks => Environment.ProcessorCount;
static int DoSomething()
{
int milliSeconds = random.Next(4000) + 1000;
Console.WriteLine($"Waiting for {milliSeconds} milliseconds...");
Thread.Sleep(milliSeconds);
return milliSeconds; // make available to caller as t.Result
}
static Random random = new Random();
static void PlayMelodyWhileTasksAreRunning()
{
Console.Beep(587, 200); // D
Console.Beep(622, 200); // D#
Console.Beep(659, 200); // E
Console.Beep(1047, 400); // C
Console.Beep(659, 200); // E
Console.Beep(1047, 400); // C
Console.Beep(659, 200); // E
Console.Beep(1047, 1200); // C
Console.Beep(1047, 200); // C
Console.Beep(1175, 200); // D
Console.Beep(1245, 200); // D#
Console.Beep(1319, 200); // E
Console.Beep(1047, 200); // C
Console.Beep(1175, 200); // D
Console.Beep(1319, 400); // E
Console.Beep(988, 200); // H
Console.Beep(1175, 400); // D
Console.Beep(1047, 1600); // C
}
}

You can use the ContinueWith function with your routine as a first argument, and a task scheduler as the second argument given by TaskScheduler.FromCurrentSynchronizationContext().
It goes like this:
var task1 = new Task(() => {do_something_in_a_remote_thread();} );
task1.ContinueWith(() => {do_something_in_the_ui_thread();},
TaskScheduler.FromCurrentSynchronizationContext());

Related

Why does my code run on multiple threads?

Since a pretty long time I'm trying to understand async-await stuff in .NET, but I struggle to succeed, there's always something totally unexpected happening when I use async.
Here's my application:
namespace ConsoleApp3
{
class Program
{
static async Task Main(string[] args)
{
Console.WriteLine("Hello World!");
var work1 = new WorkClass();
var work2 = new WorkClass();
while(true)
{
work1.DoWork(500);
work2.DoWork(1500);
}
}
}
public class WorkClass
{
public async Task DoWork(int delayMs)
{
var x = 1;
await Task.Delay(delayMs)
var y = 2;
}
}
}
It's just a sample that I created to check how the code will be executed. There are a few things that surprise me.
First off, there are many threads involved! If I set a breakpoint on var y = 2; I can see that threadId is not the same there, it can be 1, or 5, or 6, or something else.
Why is that? I thought that async/await does not use additional threads on its own unless I explicitly command that (by using Task.Run or creating a new Thread). At least this article tries to say that I think.
Ok, but let's say that there are some other threads for whatever reason - even if they are, my await Task.Delay(msDelay); does not have ConfigureAwait(false)! As I understand it, without this call, thread shouldn't change.
It's really difficult for me to grasp the concept well, because I cannot find any good resource that would contain all information instead of just a few pieces of information.
When an asynchronous method awaits something, if it's not complete, it schedules a continuation and then returns. The question is which thread the continuation runs on. If there's a synchronization context, the continuation is scheduled to run within that context - typically a UI thread, or potentially a specific pool of threads.
In your case, you're running a console app which means there is no synchronization context (SynchronizationContext.Current will return null). In that case, continuations are run on thread pool threads. It's not that a new thread is specifically created to run the continuation - it's just that the thread pool will pick up the continuation, whereas the "main" thread won't run the continuation.
ConfigureAwait(false) is used to indicate that you don't want to return to the current synchronization context for the continuation - but as there's no synchronization context anyway in your case, it would make no difference.
Async/await does not use additional threads on its own, but in your example it is not on its own. You are calling Task.Delay, and this method schedules a continuation to run in a thread-pool thread. There is no thread blocked during the delay though. A new thread is not created. When the time comes an existing thread is used to run the continuation, which in your case has very little work to do (just run the var y = 2 assignment), because you are not even awaiting the task returned by DoWork. When this work is done (a fraction of a microsecond later) the thread-pool thread is free again to do other jobs.
Instead of Task.Delay you could await another method that makes no use of threads at all, or a method that creates a dedicated long running thread, or a method that starts a new process. Async/await is not responsible for any of these. Async/await is just a mechanism for creating task continuations in a developer-friendly way.
Here is your application modified for a world without async/await:
class Program
{
static Task Main(string[] args)
{
Console.WriteLine("Hello World!");
var work1 = new WorkClass();
var work2 = new WorkClass();
while (true)
{
work1.DoWork(500);
work2.DoWork(1500);
}
}
}
public class WorkClass
{
public Task DoWork(int delayMs)
{
var x = 1;
int y;
return Task.Delay(delayMs).ContinueWith(_ =>
{
y = 2;
});
}
}

Can I await on IProgress.Report

I'm following the pattern described at: https://blogs.msdn.microsoft.com/dotnet/2012/06/06/async-in-4-5-enabling-progress-and-cancellation-in-async-apis/ so I'm using IProgress.Report in my async method to report back the progress.
public async Task MyFirstTaskAsync(IProgress<T> progress)
{
progress.report(some T)
}
public async Task MySecondTaskAsync(IProgress<T> progress)
{
progress.report(some T)
}
On the UI thread, I'll call MyFirstTaskAsync and MySecondTaskAsync sequentially.
var reportProgress1 = new Action<T> (x =>Console.WriteLine("First Step Completed"))
var reportProgress2 = new Action<T> (x =>Console.WriteLine("Last Step Completed"))
var progress1 = new Progress<T>(reportProgress1 )
var progress2 = new Progress<T>(reportProgress2 )
await MyFirstTaskAsync(progress1)
Console.WriteLine("Second Step Comppleted")
await MySecondTaskAsync(progress2)
//code in reportProgress1.report can actually be executed at this point...
My problem here is code in reportProgress1.report can actually get executed after MyFirstTaskAsync has completed, which kinda messes up the progress report because I was expecting the first await can await until the progress report inside the first async method is also completed.
Is this a behavior I can somehow tweak to fit my need?
Thanks.
Edit: Suppose there're some non-async code between two tasks, and these three together are completing the overall process and each of them will update the progress as they run so they'll have to update the UI in a sequential order..
No, you can't "await" IProgress.Report. Progress reports are essentially fire-and-forget.
You can, however, use your own implementation of IProgress with appropriate semantics. In particular, Reactive Extensions is good for taming "streams of data" (in this case, streams of progress reports).

Asynchronous calls with delegate

I'm currently using delegate to make asynchronous calls in a for loop, the issue I'm running into is - how do I know when those asynchronous calls are finished?
for example:
public delegate string GetMergeSectionCaller(string something1, out int threadId);
public Dataset GetDataset (param1, param2) {
int threadId;
Dataset ds = new Dataset;
using (myConnection) {
myConnection.StartConnection();
GetMergeSectionCaller caller = new GetMergeSectionCaller(GetMergeSection);
foreach (var r in anObjectList) {
IAsyncResult result = caller.BeginInvoke(r.ToString(), out threadId, null, null);
}
//I want to do here is wait for the above every single foreach BeginInvoke to finish; then do the below job
ds = GetFinalData();
}
//do more thing to ds here;
return ds;
}
public void GetMergeSectionCaller(string something1, out int threadId) {
//doing superlong long job
//in my actual case, it's actually inserting data to db etc
Thread.Sleep(5000);
threadId = Thread.CurrentThread.ManagedThreadId;
}
so I tried different approaches; such as passing callback to my BeginInvoke and EndInvoke there, but still - I'm missing a way to stop the rest of the code to run before I could finish up the foreach;
maybe I'm missing something pretty simple there?.... Could someone please show me a full working sample based on my condition?
You have a few options. The IAsyncResult has a WaitHandle property which you can use to wait on.
var results = new List<WaitHandle>();
foreach (var r in anObjectList) {
results.Add(caller.BeginInvoke(r.ToString(), out threadId, null, null).WaitHandle);
}
// wait for all results to complete
WaitHandle.WaitAll(results.ToArray());
Another option would be to create a ManualResetEvent and a counter and reset the event from the callback when the counter reaches 0. The advantage of this method is that you would only be creating a single waitable object but you'd have to also manage the counter.
And finally, another option would be to use the new Task-based API which provides a much better programming abstraction for waiting on tasks.
Some other things to point out:
DO NOT use Thread.Sleep - it's ok to use it to test your code but once you've verified that your asynchronous code works, do not use it!
DO NOT rely on the delegate BeginInvoke - that's not true parallelism. It simply defers the invocation of the method but it doesn't do what you think it does. Instead, if you want to execute those methods in parallel, use either a Task, the ThreadPool, or a Thread.
UPDATE
You may also use a TPL parallel for loop which might be closer to what you originally were hoping to achieve:
Parallel.ForEach(anObjectList, anObjectItem => {
// do something with anObjectItem
});
// this parallelizes the for-loop iterations
UPDATE 2
Here's how to run the tasks using worker threads from the ThreadPool and a ManualResetEvent.
ManualResetEvent mreComplete = new ManualResetEvent(false);
int callsRemaining;
GetMergeSectionCaller caller = new GetMergeSectionCaller(GetMergeSection);
callsRemaining = anObjectList.Count;
mreComplete.Reset();
foreach (var r in anObjectList) {
ThreadPool.QueueUserWorkItem((Action)delegate {
caller(r.ToString());
lock{
if(--callsRemaining==0) mreComplete.Set();
}
}
}
mreComplete.Wait();

Check if at least one thread is completed

First of all I am totally new to threading in C#. I have created multiple threads as shown below.
if (flag)
{
foreach (string empNo in empList)
{
Thread thrd = new Thread(()=>ComputeSalary(empNo));
threadList.Add(thrd);
thrd.Start();
}
}
Before proceeding further I need check if at least one thread is completed its execution so that I can perform additional operations.
I also tried creating the list of type thread and by added it to list, so that I can check if at least one thread has completed its execution. I tried with thrd.IsAlive but it always gives me current thread status.
Is there any other way to check if atleast on thread has completed its execution?
You can use AutoResetEvent.
var reset = new AutoResetEvent(false); // ComputeSalary should have access to reset
.....
....
if (flag)
{
foreach (string empNo in empList)
{
Thread thrd = new Thread(()=>ComputeSalary(empNo));
threadList.Add(thrd);
thrd.Start();
}
reset.WaitOne();
}
.....
.....
void ComputeSalary(int empNo)
{
.....
reset.set()
}
Other options are callback function, event or a flag/counter(this is not advised).
Here is a solution based on the Task Parallel Library:
// Create a list of tasks for each string in empList
List<Task> empTaskList = empList.Select(emp => Task.Run(() => ComputeSalary(emp)))
.ToList();
// Give me the task that finished first.
var firstFinishedTask = await Task.WhenAny(empTaskList);
A couple of things to note:
In order to use await inside your method, you will have to declare it as async Task or or async Task<T> where T is the desired return type
Task.Run is your equivalent of new Thread().Start(). The difference is Task.Run will use the ThreadPool (unless you explicitly tell it not to), and the Thread class will construct an entirely new thread.
Notice the use of await. This tells the compiler to yield control back to the caller until Task.WhenAny returns the first task that finished.
You should read more about async-await here

Instance of Task class (Task.Factory.StartNew or TaskCompletionSource)

This is probably a pretty basic question, but just something that I wanted to make sure I had right in my head.
Today I was digging with TPL library and found that there are two way of creating instance of Task class.
Way I
Task<int> t1 = Task.Factory.StartNew(() =>
{
//Some code
return 100;
});
Way II
TaskCompletionSource<int> task = new TaskCompletionSource<int>();
Task t2 = task.Task;
task.SetResult(100);
Now,I just wanted to know that
Is there any difference between these instances?
If yes then what?
The second example does not create a "real" task, i.e. there is no delegate that does anything.
You use it mostly to present a Task interface to the caller. Look at the example on
msdn
TaskCompletionSource<int> tcs1 = new TaskCompletionSource<int>();
Task<int> t1 = tcs1.Task;
// Start a background task that will complete tcs1.Task
Task.Factory.StartNew(() =>
{
Thread.Sleep(1000);
tcs1.SetResult(15);
});
// The attempt to get the result of t1 blocks the current thread until the completion source gets signaled.
// It should be a wait of ~1000 ms.
Stopwatch sw = Stopwatch.StartNew();
int result = t1.Result;
sw.Stop();
Console.WriteLine("(ElapsedTime={0}): t1.Result={1} (expected 15) ", sw.ElapsedMilliseconds, result);
As you are not firing any async operation in Way 1 above, you are wasting time by consuming another thread from the threadpool (possibly, if you don't change the default TaskScheduler).
However, in the Way 2, you are generating a completed task and you do it in the same thread that you are one. TCS can been also seen as a threadless task (probably the wrong description but used by several devs).

Categories

Resources