1

I have the following code to help my understanding of multiple threading, the purpose of it is to create 3 background worker threads with debug code to display thread usage/availablity. Now code seems ok from what I can see but I sometimes get unexpected results.

Calling Code :

 static void Main(string[] args) { ThreadPool.CreatWorkBetter(); Console.ReadLine(); } 

Implementation Code:

using System; using System.Collections.Generic; using System.Linq; using System.Text; using t = System.Threading; namespace CSharpConcepts { public static class ThreadPool { private static t.ManualResetEvent[] resetEvent; public static void CreatWorkBetter() { Console.WriteLine("Start"); ListAvailableThreads(); resetEvent = new t.ManualResetEvent[3]; resetEvent[0] = new t.ManualResetEvent(false); resetEvent[1] = new t.ManualResetEvent(false); resetEvent[2] = new t.ManualResetEvent(false); t.ThreadPool.QueueUserWorkItem( new t.WaitCallback(delegate(object state) { PooledFunc("Stage 1", resetEvent[0]); })); t.ThreadPool.QueueUserWorkItem( new t.WaitCallback(delegate(object state) { PooledFunc("Stage 2", resetEvent[1]); })); t.ThreadPool.QueueUserWorkItem( new t.WaitCallback(delegate(object state) { PooledFunc("Stage 3", resetEvent[2]); })); t.WaitHandle.WaitAll(resetEvent); Console.WriteLine("Finished"); ListAvailableThreads(); } static void PooledFunc(object state, t.ManualResetEvent e) { Console.WriteLine("Processing request '{0}'", (string)state); // Simulation of processing time t.Thread.Sleep(2000); Console.WriteLine("Request processed"); ListAvailableThreads(); e.Set(); } public static void ListAvailableThreads() { int avlThreads, avlToAsynThreads; t.ThreadPool.GetAvailableThreads(out avlThreads, out avlToAsynThreads); string message = string.Format("Processed request: {3}, From ThreadPool :{0} ,Thread Id :{1},Free Threads :{2}",t.Thread.CurrentThread.IsThreadPoolThread,t.Thread.CurrentThread.ManagedThreadId,avlThreads,t.Thread.CurrentThread.ThreadState); Console.WriteLine(message); } } } 

What I expect results wise is this and most of the time I get it( the critical line shown with free threads being back to 1023 is what I really want to see):

Start Processed request: Running, From ThreadPool :False ,Thread Id :1,Free Threads :1023 Processing request 'Stage 1' Processing request 'Stage 2' Processing request 'Stage 3' Request processed Processed request: Background, From ThreadPool :True ,Thread Id :4,Free Threads :1020 Request processed Processed request: Background, From ThreadPool :True ,Thread Id :3,Free Threads :1021 Request processed Processed request: Background, From ThreadPool :True ,Thread Id :5,Free Threads :1022 Finished Processed request: Running, From ThreadPool :False ,Thread Id :1,Free Threads :1023

However, I sometimes get with the free threads showing 1022, I would hope it's 1023 since 3 threads have completed the work so they should have been returned to the thread pool:

Start Processed request: Running, From ThreadPool :False ,Thread Id :1,Free Threads :1023 Processing request 'Stage 1' Processing request 'Stage 2' Processing request 'Stage 3' Request processed Processed request: Background, From ThreadPool :True ,Thread Id :3,Free Threads :1020 Request processed Processed request: Background, From ThreadPool :True ,Thread Id :4,Free Threads :1020 Request processed Processed request: Background, From ThreadPool :True ,Thread Id :5,Free Threads :1022 Finished Processed request: Running, From ThreadPool :False ,Thread Id :1,Free Threads :1022

Any ideas?

1
  • Are you sure it is one of your worker threads that isn't free? Could it be the garbage collector? Commented Feb 7, 2012 at 18:29

3 Answers 3

2

Your last thread calls e.Set() as it's last line, which frees up the main thread from it's WaitAll(), but that doesn't mean that the last worker thread has exited the CreatWorkBetter method yet. Once in a while, the main thread is waking up and counting the number of active worker threads even before the last worker gets from the e.Set() call to exiting the method.

So my last paragraph stands:

This is the critical thing to understand about threading- you cannot make assumptions about the relative state of other threads from within a method unless you use synchronization constructs to control each thread's state.

Sign up to request clarification or add additional context in comments.

6 Comments

Rubans is calling ListAvailableThreads at the end of CreatWorkBetter in the main thread.
HI Chris, Thanks for the reply. I assume you meant 1023 since this is the max number of threads available in .net 4. Also, I did move ListAllAvailableThreads outside and still same issue. Even from the output of the original code, From ThreadPool :False is still displayed so I assume this being called from outside of ThreadPool anwyay.
@Rubans I apologize, this is clearly not the issue. I missed the fact that you were calling this in the CreatWorkBetter method.
@ChrisShain, I see you what you are saying and it does make sense, I shall do some research in checking for this. Any suggestions?
Put an a boolean parameter in ListAvailableThreads. Pass true from the CreatWorkBetter method, false from PooledFunc. Put a conditional breakpoint on the line string message = string.Format("Processed request..., that only breaks when the parameter is true and Free Threads = 1022. Finally, run it until you hit the breakpoint. At that point, examine the threads in the debugger's Threads window.
|
1

You are running into the case where two of your threads have terminated and one thread has called e.Set() at the end of PooledFunc but has not yet terminated. Meanwhile the main thread's WaitAll(resetEvent) has returned and the main thread is calling ListAvailableThreads().

5 Comments

I believe you are right, that does make sense and this what I was thinking but my understanding of threads is basic. Any suggestions on what I can do to get around it?
Why do you need to get around this? What is the problem you want to solve? Why do you need to know exactly that all threads have finished work - it's a worker thread pool, giving you an abstraction.
I don't really need to have it returning 1023 for any sort of functional requirement apart from my own obsessive compulsive nature :)
OK :). Then my advise: live with it, this is by design. Your worker delegates are signalling the events when they have done their work. The threads are marked as free when the delegates have terminated. Both are nearly the same but yet different things. If you want to change that you could try using the Task<T> class. Or if you want it the hard way you can start threads yourself and wait for them to terminate.
Yeah, it's fine. I started looking into it in order start using Tasks in c#4.5 but wanted to at least have a basic understanding of threads which I sort of have now I think before I look at Tasks and Async stuff.
0

I haven't tried much threading in C# but I've done a decent bit in Java. I would assume the call to GetAvailableThreads is not atomic, nor is it locked so threads could be changing the count simultaneously.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.