Pergunta

I was reading AutoResetEvent documentation on MSDN and following warning kinda bothers me..

"Important: There is no guarantee that every call to the Set method will release a thread. If two calls are too close together, so that the second call occurs before a thread has been released, only one thread is released. It is as if the second call did not happen. Also, if Set is called when there are no threads waiting and the AutoResetEvent is already signaled, the call has no effect."

But this warning basically kills the very reason to have such a thread synchronization techniques. For example I have a list which will hold jobs. And there is only one producer which will add jobs to the list. I have consumers (more than one), waiting to get the job from the list.. something like this..

Producer:

void AddJob(Job j)
{
    lock(qLock)
    {
        jobQ.Enqueue(j);
    }

    newJobEvent.Set(); // newJobEvent is AutoResetEvent
}

Consumer

void Run()
{
    while(canRun)
    {
        newJobEvent.WaitOne();

        IJob job = null;

        lock(qLock)
        {
            job = jobQ.Dequeue();
        }

        // process job
    }
}

If the above warning is true, then if I enqueue two jobs very quickly, only one thread will pick up the job, isn't it? I was under the assumption that Set will be atomic, that is it does the following:

  1. Set the event
  2. If threads are waiting, pick one thread to wake up
  3. reset the event
  4. run the selected thread.

So I am basically confused about the warning in MSDN. is it a valid warning?

Foi útil?

Solução

Even if the warning isn't true and Set is atomic, why would you use an AutoResetEvent here? Let's say you have some producers queue up 3 events in row and there's one consumer. After processing the 2nd job, the consumer blocks and never processes the third.

I would use a ReaderWriterLockSlim for this type of synchronization. Basically, you need multiple producers to be able to have write locks, but you don't want consumers to lock out producers for a long time while they are only reading the queue size.

Outras dicas

The message on MSDN is a valid message indeed. What's happening internally is something like this:

  1. Thread A waits for the event
  2. Thread B sets the event
  3. [If thread A is in spinlock]
    1. [yes] Thread a detects that the event is set, unsets it and resumes its work
    2. [no] The event will tell thread A to wake up, once woken, thread A will unset the event resume its work.

Note that the internal logic is not synchronous since Thread B doesn't wait for Thread A to continue its business. You can make this synchronous by introducing a temporary ManualResetEvent that thread A has to signal once it continues its work and on which Thread B has to wait. This is not done by default due to the inner working of the windows threading model. I guess the documentation is misleading but correct for saying that the Set method only releases one or more waiting threads.

Alternatively i would suggest you to look at the BlockingCollection class in the System.Collections.Concurrent namespace of the BCL introduced in .NET 4.0 which does exactly what you are trying to do

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top