C# multi-threaded console application - Console quits before threads complete
I have a c# console application that creates up to 5 threads.
The threads are executing fine, but the UI thread shuts down as it finishes its work.
Is there a way to keep the main UI thread running, for as long as the side threads are running?
foreach (var url in urls开发者_开发技巧)
{
Console.WriteLine("starting thread: " + url);
ThreadPool.QueueUserWorkItem(new System.Threading.WaitCallback(myMethod), url);
}
I'm kicking off my threads as per the code above.
The threads in the ThreadPool are background threads and that means an exiting application won't wait for them to complete.
You have a few options:
- wait with for example a semaphore
- wait on a counter with Sleep() , very crude but OK for a simple console app.
- use the TPL,
Parallel.ForEach(urls, url => MyMethod(url));
If you are using .NET 4.0:
var tasks = new List<Task>();
foreach(var url in urls)
{
tasks.Add(Task.Factory.StartNew(myMethod, url));
}
// do other stuff...
// On shutdown, give yourself X number of seconds to wait for them to complete...
Task.WaitAll(tasks.ToArray(), TimeSpan.FromSeconds(30));
Ah - the ThreadPool is background. It is queued, but then your program ends. Finished. Program terminates.
Read up on Semaphores (WaitSignal) and wait- The threads in the callback at the end signal they are ended, when all have signaled that the main thread can continue.
If you're using .net 4 then:
urls.AsParallel().ForAll(MyMethod);
Prior to .net 4 then start individual threads, keep them in a list and call Join(). The fact the workers are not background would keep them alive after the main thread exited, but the Join() is more explicit.
List<Thread> workers = new List<Thread>();
foreach(var url in urls)
{
Thread t = new Thread(MyMethod) {IsBackground = false};
workers.Add(t);
t.Start(url);
}
foreach (var worker in workers)
{
worker.Join();
}
Simplest hack to fix your problem.
In your program class:
static volatile int ThreadsComplete = 0;
In your "myMethod" at the end before return:
//ThreadsComplete++; //*edit* for safety's sake
Interlocked.Increment(ref ThreadsComplete);
In your main method before it returns/ends:
while(ThreadsComplete < urls.Count) { Thread.Sleep(10); }
The above essentially hacks together a WaitForAll synchronization method.
in Main:
var m = new ManualResetEvent(false);
// do something
foreach (var url in urls)
{
Console.WriteLine("starting thread: " + url);
ThreadPool.QueueUserWorkItem(new System.Threading.WaitCallback(myMethod), url);
}
m.WaitOne();
private static void myMethod(object obj)
{
try{
// do smt
}
finally {
m.Set();
}
}
精彩评论