4

I'm starting multiple threads and would like to know when any of then finishes. I know the following code:

foreach (Thread t in threads)
    t.Join();

But it will only wait for all threads together. That's much too late. I need to know when one thread finishes, even when other threads are still running. I'm looking for something equivalent to WaitAny only for threads. But I can't add code to all threads I'm monitoring, so using signals or other synchronisation objects is not an option.

Some clarification: I'm working on a logging/tracing tool that should log the application's activity. I can insert log statements when a thread starts, but I can't insert a log statement on every possible way out of the thread (multiple exit points, exceptions etc.). So I'd like to register the new thread and then be notified when it finishes to write a log entry. I could asynchronously Join on every thread, but that means a second thread for every monitored thread which may seem a bit much overhead. Threads are used by various means, be it a BackgroundWorker, Task or pool thread. In its essence, it's a thread and I'd like to know when it's done. The exact thread mechanism is defined by the application, not the logging solution.

ygoe
  • 18,655
  • 23
  • 113
  • 210
  • This is a code smell. You won't know *which* thread completed so you can't reason about what job actually got done. – Hans Passant Aug 25 '13 at 09:16
  • I've added more background about what I'm trying to do. Of course I need to know which thread has finished, otherwise it would be useless in this case. – ygoe Aug 25 '13 at 09:17
  • I go with Hans on this one. Why has your thread code got multiple exit points? Put a try/catch round ALL of the thread code and, if you want to exit somewhere in the middle, throw. All my other doubts are covered by the existing answers - why are you continually creating/terminating/destroying threads instead of using pools or app-lifetime threads that just get signaled? – Martin James Aug 25 '13 at 09:56
  • Again, I am not over control of what the application does with threads. My goal is just to provide a logging solution that will write a log entry whenever a thread has finished. If I had full control over what it is that shall be logged, then, of course, I could do what you suggest. – ygoe Aug 25 '13 at 12:46

5 Answers5

5

Instead of Threads use Tasks. It has the method WaitAny.

Task.WaitAny

As you can read here,

  • More efficient and more scalable use of system resources.
  • More programmatic control than is possible with a thread or work item.
I4V
  • 34,891
  • 6
  • 67
  • 79
  • 1
    Please see my clarification edit. I only have `Thread.Current`, however it was started. – ygoe Aug 25 '13 at 09:20
1

In my opinion WaitHandle.WaitAny is the best solution, since you don't like to use it for some xyz reason you can try something like this.

Take the advantage of Thread.Join(int) method which takes millisecond timeout and returns true when thread is terminated or false when timed out.

List<Thread> threads = new List<Thread>();

while (!threads.Any(x=> x.Join(100)))
{

}

You can alter the timeout of Join If you know how long it will take.

Sriram Sakthivel
  • 72,067
  • 7
  • 111
  • 189
  • I thought that this would be proposed (forgot to mention it). I'm just worried about the constant activity on the monitoring thread which may increase power usage. To get a decent timing resolution I think I'd need no more than 100 ms. – ygoe Aug 25 '13 at 09:19
  • So, does this answers your question or you're looking for something else – Sriram Sakthivel Aug 25 '13 at 09:33
  • You should check IsAlive ... Join will just invoke a Join on each of them. Besides, you should send the thread to sleep in each iteration, or else you'll just be in a spin. – Yochai Timmer Aug 25 '13 at 10:11
  • I don't think Join(100) results in spin internally. I think sleep is irrelevant here since Join is similar to a wait which won't be expensive – Sriram Sakthivel Aug 25 '13 at 10:16
  • 1) This is doing a busy wait, which is conceptually a very bad idea that should be avoided whenever possible. 2) You're not "sleeping" by 100ms per loop, you sleeping by 100ms per thread per loop. If you have 10 threads in the loop then it takes you a full second to check each one. (And that's minimum, which of course won't actually happen. It'll likely take noticeably longer than that.) Waiting for such a long time after one of the threads stop to continue on is very likely to be problematic. – Servy Dec 12 '13 at 18:05
  • @Servy You mean `join` is busy wait or `while` ? and I agree with you on the other hand I mentioned you can alter sleep time as you like! If you have better answer for this question without using TPL, etc am really pleased to see that. – Sriram Sakthivel Dec 12 '13 at 19:09
  • @SriramSakthivel The act of having a while loop that simply checks if each item is finished is conceptually a busy wait; you're spending 100ms per item to do the check, rather than doing it as fast as possible, but the concept remains. – Servy Dec 12 '13 at 19:50
  • @Servy Agree.. I'd like to hear any better solution if available in this scenario? – Sriram Sakthivel Dec 12 '13 at 19:55
1

My answer is based on your clarification that all you have is Thread.Current. Disclaimer: IMO, what you're trying to do is a hack, thus my idea by all means is a hack too.

So, use reflection to obtain the set of native Win32 handles for your desired threads. Your are looking for Thread.GetNativeHandle method which is internal, so you call it like thread.GetType().InvokeMember("GetNativeHandle", BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.NonPublic, ...). Use a reflection tool of your choice or Framework sources to learn more about it. Once you've got the handles, go on with one of the following options:

  • Set up your own implementation of SynchronizationContext (derive from it) and use SynchronizationContext.WaitHelper(waitAll: false) to wait for your unmanaged handles.

  • Use the raw Win32 API like WaitForMultipleObjects or CoWaitForMultipleObjects (depending on whether you need to pump messages).

Perform the wait on a separate child or pool thread.

[EDITED] Depending on the execution environment of your target threads, this hack may not work, because one-to-one mapping between managed and unmanaged threads is not guaranteed:

It is possible to determine the Windows thread that is executing the code for a managed thread and to retrieve its handle. However, it still doesn't make sense to call the SetThreadAffinityMask function for this Windows thread, because the managed scheduler can continue the execution of a managed thread in another Windows thread.

It appears however, this may be an implication only for custom CLR hosts. Also, it appears to be possible to control managed thread affinity with Thread.BeginThreadAffinity and Thread.EndThreadAffinity.

Community
  • 1
  • 1
noseratio
  • 59,932
  • 34
  • 208
  • 486
0

You could use a background worker for your working threads.

Then hook all the RunWorkerCompleted events to a method that will wait for them.

If you want that to be synched to the code where you're currently waiting for the join, then the problem is reduced to just synchronizing that single event method to that place in code.

Better yet, I'd suggest to do what you're doing asynchronously without blocking, and just do what you want in the event.

Yochai Timmer
  • 48,127
  • 24
  • 147
  • 185
0

Would you consider wrapping your thread invocations with another 'logging' thread? That way you could log synchronously before & after the thread run.

Something like this pseudo-code:

int threadLogger(<parms>) {
    log("starting thread");
    retcode = ActualThreadBody(<parms>);
    log("exiting thread");
    return retcode;
}

If you have more information on the thread started, you could log that as well. You could also take the thread function as a parameter in the case where you have multiple types of threads to start, which it sounds like you do.

MrWonderful
  • 2,530
  • 1
  • 16
  • 22