Notify When Thread Is Complete, Without Locking Calling Thread

Notify when thread is complete, without locking calling thread

There are two slightly different kinds of requirement here:

  • Execute a callback once the long-running task has completed
  • Execute a callback once the thread in which the long-running task was running has completed.

If you're happy with the first of these, the simplest approach is to create a compound task of "the original long-running task, and the callback", basically. You can even do this just using the way that multicast delegates work:

ThreadStart starter = myLongRunningTask;
starter += () => {
// Do what you want in the callback
};
Thread thread = new Thread(starter) { IsBackground = true };
thread.Start();

That's very vanilla, and the callback won't be fired if the thread is aborted or throws an exception. You could wrap it up in a class with either multiple callbacks, or a callback which specifies the status (aborted, threw an exception etc) and handles that by wrapping the original delegate, calling it in a method with a try/catch block and executing the callback appropriately.

Unless you take any special action, the callback will be executed in the background thread, so you'll need to use Control.BeginInvoke (or whatever) to marshal back to the UI thread.

Notify consumers when all tasks have completed without blocking the thread

First off, don't use the Task constructor at all. If you want to run a delegate in a thread pool thread, use Task.Run. Next, don't use ContinueWith, use await to add continuations to tasks. It's much easier to write correct code that way, particularly with respect to proper error handling. Your code is most effectively written as:

try
{
await Task.Run(() => Execute(action));
Continuation();
}
catch(Exception e)
{
LogExceptions(e)
}
finally
{
CustomLogging();
}

Why do we need notify() for inter thread communication

Good question. Will point you to take a look at the Thread State Class.

A thread that calls the Object.notify method enables a thread that previously called Object.wait is now enabled to be scheduled by the thread scheduler. In parlance, the thread that was waiting is now "runnable". Although it is "runnable", it is not "running".

It can only continue running when the thread invoking notify releases the lock - one way is when it exits out of the synchronized block.

There are a lot of schematics on the web on the Thread States. Some of them are completely incorrect or confusing since they introduce terminology not in the official docs. Here is one that makes sense to me.

How to wait for thread to complete without blocking UI

I'm very surprised you haven't worked with any of these before but I would really recommend reading about threading in C# since it's fundamentally important to understand the intricacies and learning the language.

Below are three different ways you can achieve what you want:

1. Using reset events (further reading: https://msdn.microsoft.com/en-us/library/system.threading.manualreseteventslim(v=vs.110).aspx). If your C# version doesn't have the ManualResetEventSlim, replace it with ManualResetEvent and change Wait() with WaitOne()

class LockingWithResetEvents
{
private readonly ManualResetEvent _resetEvent = new ManualResetEvent(false);

public void Test()
{
MethodUsingResetEvents();
}

private void MethodUsingResetEvents()
{
ThreadPool.QueueUserWorkItem(_ => DoSomethingLong());
ThreadPool.QueueUserWorkItem(_ => ShowMessageBox());
}

private void DoSomethingLong()
{
Console.WriteLine("Doing somthing.");
Thread.Sleep(1000);
_resetEvent.Set();
}

private void ShowMessageBox()
{
_resetEvent.WaitOne();
Console.WriteLine("Hello world.");
}
}

2) Using Task Parallel Library (TPL). Further reading: https://msdn.microsoft.com/en-us/library/dd460717(v=vs.110).aspx

class LockingWithTPL
{
public void Test()
{
Task.Factory.StartNew(DoSomethingLong).ContinueWith(result => ShowMessageBox());
}

private void DoSomethingLong()
{
Console.WriteLine("Doing somthing.");
Thread.Sleep(1000);
}

private void ShowMessageBox()
{
Console.WriteLine("Hello world.");
}
}

3) Using Async/Await. Further reading: https://msdn.microsoft.com/en-us/library/hh191443.aspx

class LockingWithAwait
{
public void Test()
{
DoSomething();
}

private async void DoSomething()
{
await Task.Run(() => DoSomethingLong());
ShowMessageBox();
}

private async void DoSomethingLong()
{
Console.WriteLine("Doing somthing.");
Thread.Sleep(10000);
}

private void ShowMessageBox()
{
Console.WriteLine("Hello world.");
}
}

Also good to know: Mutex (https://msdn.microsoft.com/en-us/library/system.threading.mutex(v=vs.110).aspx), Semaphore (https://msdn.microsoft.com/en-us/library/system.threading.semaphore(v=vs.110).aspx), Lock (https://msdn.microsoft.com/en-us/library/c5kehkcz.aspx), SemaphoreSlim (https://msdn.microsoft.com/en-us/library/system.threading.semaphoreslim(v=vs.110).aspx), Monitor (https://msdn.microsoft.com/en-us/library/system.threading.monitor(v=vs.110).aspx) and Interlocked (https://msdn.microsoft.com/en-us/library/system.threading.interlocked(v=vs.110).aspx).

Java wait() notify() methods last thread waiting forever

You're noticing the distinction between a thread waiting to enter a synchonized block ( BLOCKED ) and a thread blocked on a wait() call ( WAITING.)

You have a race condition in your loop.You create your threads and start them. Most of them will make it to the synchronized block. Each of those threads gets to a synchronize block, grabs the lock, and releases it on the wait.

The last thread doesn't have time to run to the synchonized block before your main thread enters the synchronized block. Once your main has acquired the lock on your object, notifies a Thread waiting on the lock. That thread wakes up and acquires the lock, without letting the last thread into the synchronized block.

The first five threads finish and notify, and one thread notifies nobody. Then the last thread has a chance to enter the synchronized block and call wait, but it will never get notified.

That is why the Thread.sleep helps, because your last thread will have time to reach the synchronized block and wait.

What happens if I call notify on a thread that is not waiting?

The short answer is that nothing happens.

The slightly long answer is that if there is nothing waiting on the monitor, there is nothing for the notification to be delivered to, and the notification is silently discarded.

The monitor that you keep hearing about is just a technical term for the primitive locking mechanism that you are using. (IIRC the term was coined by the inventor of the monitor concept - Tony Hoare - who did a lot of the seminal work on concurrency.)

The idea is that there certain regions of code (in Java, they are synchronized method bodies and synchronized blocks) which a thread can only execute if it holds an exclusive lock. Other threads wanting to enter these regions have to wait for the lock to become available. The wait and notify methods provide an additional signalling mechanism that is used in conjunction with monitors.

What happens with the thread that calls notify

Nothing happens to the current thread that calls notify(), it continues to run until it's natural end.

The wait() and notify() methods must be called within a synchronized context. As soon as the synchronized block that contains the notify() call finishes, the lock is then available and the block containing the wait() call in another thread can then continue.

Calling notify simply moves the waiting thread back into the runnable thread pool. That thread can then continue as soon as the lock is available.



Related Topics



Leave a reply



Submit