简体   繁体   中英

Possible Race condition with ManualResetEvent

Problem:

I am trying to throw 6 threads from ThreadPool to work on individual tasks. Each task's ManualResetEvent is stored in a array of manual reset event. Number of thread corresponds to the index in the ManualResetEvent Array.

Now what happens is that once I have initiated these 6 threads I move out and wait for the threads to complete. Waiting for the thread is done in the main thread.

Now some times what happens is that my waiting logic doesn't return even after a long time (2 days that I have seen). Here is the code sample for thread wait logic

                foreach (ManualResetEvent whandle in eventList)
                {
                    try
                    {
                        whandle.WaitOne();
                    }
                    catch (Exception) { }
                }

As per documentation of .WaitOne. It is sync call which makes the thread to not return if Set event is not received from the thread.

Sometimes my threads have less amount of work and they may even return before I reach the Wait logic. Is it possible that .WaitOne() will wait for the Set() event even if it was received in the past? Is this a correct logic to wait for the all the threads to close?

(Note: I think your best bet is Parallel.Invoke() - see later in this answer.)

What you are doing will normally work fine, so the problem is likely to be that one of your threads is blocking for some reason.

You should be able to debug this readily enough - you can attach the debugger and break into the program and then look at the call stack to see which thread(s) are blocked. Be prepared for some head-scratching if you discover a race condition though!

Another thing to be aware of that you can't do the following:

myEvent.Set();
myEvent.Reset();

with nothing (or very little) between the .Set() and the .Reset() . If you do that when several threads are waiting on myEvent , some of them will miss the event being set! (This effect is not well documented on MSDN.)

By the way, you shouldn't ignore exceptions - always log them in some way, at the very least.


(This section doesn't answer the question, but it may provide some helpful information)

I also want to mention an alternative way to wait for the threads. Since you have a set of ManualResetEvents, you can copy them to a plain array and pass it to WaitHandle.WaitAll() .

Your code could look a little like this:

WaitHandle.WaitAll(eventList.ToArray());

Another approach to waiting for all threads to finish is to use a CountdownEvent . It becomes signalled when a countdown reaches zero; you start the count at the number of threads, and each thread signals it when it exits. There's an example here .

Parallel.Invoke()

If your threads do not return values, and all you want to to is to launch them and then have the launching thread wait for them to exit, then I think Parallel.Invoke() will be the best way of all. It avoids you having to handle the synchronization yourself.

(Otherwise, as svick says in the comments above, use Task rather than the old thread classes.)

I'm not directly answering this question. Here is what you should do:

Start tasks using Task.Factory.StartNew and use Task.WaitAll(Task[]) to wait for them. You do not have to deal with events that way. Exceptions will nicely propagate to the "forking" thread. You don't need the old ThreadPool API anymore.

Hope this helps.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM