Does Monitor.Wait require synchronization?

I have developed a general lineup of consumer producers, which implements the momentum of the Monitor as follows:

enqueue:

public void EnqueueTask(T task) { _workerQueue.Enqueue(task); Monitor.Pulse(_locker); } 

dequeue:

 private T Dequeue() { T dequeueItem; if (_workerQueue.Count > 0) { _workerQueue.TryDequeue(out dequeueItem); if(dequeueItem!=null) return dequeueItem; } while (_workerQueue.Count == 0) { Monitor.Wait(_locker); } _workerQueue.TryDequeue(out dequeueItem); return dequeueItem; } 

the following SynchronizationLockException is created in the waiting section: "the object synchronization method was called from an unsynchronized code block" do I need to synchronize it? What for? Is it better to use ManualResetEvents or Slim version of .NET 4.0?

+6
synchronization c # monitor producer-consumer
source share
3 answers

Yes, the current thread must โ€œownโ€ the monitor in order to call either Wait or Pulse , as described. (So โ€‹โ€‹you will need to block for Pulse .) I do not know the details of why this is necessary, but it is the same in Java. I usually found that I still want to do this to make the call code clean.

Note that Wait frees the monitor itself, then waits for Pulse , and then returns the monitor before returning.

Regarding the use of ManualResetEvent or AutoResetEvent instead - you could, but personally, I prefer to use the Monitor methods if I don't need some other wait descriptor functions (for example, atomic wait for any / just a few pens).

+6
source share

From the description of MSDN Monitor.Wait ():

Releases the object lock and blocks the current thread until it restores the lock.

The problem "releases the lock" - a problem, the object is not locked. You are handling the _locker object as if it were WaitHandle. Doing your own blocking construct, which seems to be correct, is a form of black magic that is best left to our medicine by Jeffrey Richter and Joe Duffy. But I will give this shot:

 public class BlockingQueue<T> { private Queue<T> queue = new Queue<T>(); public void Enqueue(T obj) { lock (queue) { queue.Enqueue(obj); Monitor.Pulse(queue); } } public T Dequeue() { T obj; lock (queue) { while (queue.Count == 0) { Monitor.Wait(queue); } obj = queue.Dequeue(); } return obj; } } 

In most producer / consumer practical scenarios, you will want to throttle the producer so that it cannot fill the queue without restrictions. As an example, consider the Duffy BoundedBuffer project . If you can afford to upgrade to .NET 4.0, then you definitely want to use your ConcurrentQueue class, it has a lot more black magic with low latency and wait expectations.

+2
source share

The correct way to view Monitor.Wait and Monitor.Pulse / PulseAll not a wait facility, but rather (for Wait ) as a means of letting the system know that the code is in a wait loop that cannot exit until something interesting changes , and (for Pulse / PulseAll ) as a means of letting the system know that the code has just changed something that could cause an exit condition, some other thread wait cycle, You need to be able to replace all occurrences of Wait with Sleep(0) and continues to work correctly (although it is much less effective in the district result of the CPU time is constantly changing test conditions that have not changed).

For this mechanism to work, you must avoid the possibility of the following sequence:

  • The code in the wait loop checks the condition when it is not satisfied.

  • Code in another thread changes the condition so that it is satisfied.

  • The code in this other thread blocks the lock (which no one else is waiting for).

  • The code in the wait loop executes Wait because its condition was not met.

The Wait method requires that the wait thread have a lock, as this is the only way to ensure that the expected condition does not change between the time it spent and the time the code executes Wait . The Pulse method requires blocking, because the only way to make sure that if another thread has completed Wait by itself, Pulse will not happen until another thread actually does. Note that using Wait inside a lock does not guarantee that it is used correctly, but there is no way that using Wait outside a lock can be correct.

The Wait / Pulse construct really works quite well if both sides work together. The biggest drawbacks of the IMHO design are: (1) there is no mechanism for the thread to wait until any of several objects are pulsed; (2) even if one of them "shuts down" the object in such a way that all future wait cycles should leave immediately (perhaps by checking the exit flag), the only way to ensure that any Wait that was sent by the stream to receive a Pulse is in order to get a lock, perhaps a long time to wait for its availability.

0
source share

All Articles