Is it safe to signal and immediately close a ManualResetEvent?

前端 未结 4 1296
借酒劲吻你
借酒劲吻你 2021-01-17 13:34

I feel like I should know the answer to this, but I\'m going to ask anyway just in case I\'m making a potentially catastrophic mistake.

The following code executes a

4条回答
  •  不思量自难忘°
    2021-01-17 14:12

    When you signal with a ManualResetEvent.Set you are guaranteed that all the threads that are waiting for that event (i.e. in a blocking state on flag.WaitOne) will be signaled before returning control to the caller.

    Of course there are circumstances when you might set the flag and your thread doesn't see it because it's doing some work before it checks the flag (or as nobugs suggested if you're creating multiple threads):

    ThreadPool.QueueUserWorkItem(s =>
    {
        QueryDB();
        flag.WaitOne();
        Console.WriteLine("Work Item 1 Executed");
    });
    

    There is contention on the flag and now you can create undefined behavior when you close it. Your flag is a shared resource between your threads, you should create a countdown latch on which every thread signals upon completion. This will eliminate contention on your flag.

    public class CountdownLatch
    {
        private int m_remain;
        private EventWaitHandle m_event;
    
        public CountdownLatch(int count)
        {
            Reset(count);
        }
    
        public void Reset(int count)
        {
            if (count < 0)
                throw new ArgumentOutOfRangeException();
            m_remain = count;
            m_event = new ManualResetEvent(false);
            if (m_remain == 0)
            {
                m_event.Set();
            }
        }
    
        public void Signal()
        {
            // The last thread to signal also sets the event.
            if (Interlocked.Decrement(ref m_remain) == 0)
                m_event.Set();
        }
    
        public void Wait()
        {
            m_event.WaitOne();
        }
    }
    
    1. Each thread signals on the countdown latch.
    2. Your main thread waits on the countdown latch.
    3. The main thread cleans up after the countdown latch signals.

    Ultimately the time you sleep at the end is NOT a safe way to take care of your problems, instead you should design your program so it is 100% safe in a multithreaded environment.

    UPDATE: Single Producer/Multiple Consumers
    The presumption here is that your producer knows how many consumers will be created, After you create all your consumers, you reset the CountdownLatch with the given number of consumers:

    // In the Producer
    ManualResetEvent flag = new ManualResetEvent(false);
    CountdownLatch countdown = new CountdownLatch(0);
    int numConsumers = 0;
    while(hasMoreWork)
    {
        Consumer consumer = new Consumer(coutndown, flag);
        // Create a new thread with each consumer
        numConsumers++;
    }
    countdown.Reset(numConsumers);
    flag.Set();
    countdown.Wait();// your producer waits for all of the consumers to finish
    flag.Close();// cleanup
    

提交回复
热议问题