Does this use Monitor.Wait / Pulse race condition?
I have a simple producer / consumer scenario where there is only one separate item being produced / consumed. In addition, the producer waits for the workflow to complete before continuing. I realize this removes the whole point of multithreading, but please just assume that it really should be like this (:
This code won't compile, but I hope you get the idea:
// m_data is initially null
// This could be called by any number of producer threads simultaneously
void SetData(object foo)
{
lock(x) // Line A
{
assert(m_data == null);
m_data = foo;
Monitor.Pulse(x) // Line B
while(m_data != null)
Monitor.Wait(x) // Line C
}
}
// This is only ever called by a single worker thread
void UseData()
{
lock(x) // Line D
{
while(m_data == null)
Monitor.Wait(x) // Line E
// here, do something with m_data
m_data = null;
Monitor.Pulse(x) // Line F
}
}
Here's a situation I'm not sure about:
Let's assume that many threads call SetData () different inputs. Only one of them will get inside the lock, and the rest will be blocked on line A. Suppose the one that got blocked sets m_data strong> and breaks through to line C.
Question. Can Wait () on line C allow another thread on line A to acquire the lock and overwrite m_data before the worker thread even hits it?
Suppose it doesn't, and the worker thread processes the original m_data and eventually jumps to line F, what happens when this pulse () turns off?
Will only the thread waiting on line C get the lock? Or will it compete with all the other strands waiting on the A line as well?
Essentially I want to know if Pulse () / Wait () communicate with each other on purpose "under the hood" or if they are on par with blocking ().
The solution to these problems, if they exist, is obviously obvious - just surround SetData () with another lock - say lock (y). I'm just wondering if this is even a problem to start with.
a source to share
There is no guarantee that a consumer will be queued in a waiting or ready queue before another producer.
C # and Java style monitors are described in Wikipedia under Implicit Condition Monitors .
Good overview of what's going on in Monitor
(taken from this excellent site):
"Can Wait () on line C allow another thread on line A to acquire the lock and overwrite m_data before the worker even gets to it?"
Suppose it SetData()
is called by two producer threads, P1 and P2 .
Consumer thread, C1 also starts.
P1 , P2 and C1 all enter the ready queue.
P1 acquires the lock first.
The wait queue is empty and Pulse()
is line B
inactive.
P1 waits in line C
, so it is put on the wait queue.
The next thread in the ready queue acquires the lock.
It can be P2 or C1 - in the first case, the assertion fails.
You have a race condition.
"Assuming it doesn't, and the worker thread processes the raw m_data and eventually jumps to line F, what happens when this Pulse () is disabled?"
It will move the waiter from the waiting queue to the ready queue.
The lock is being held by the thread that is issuing Pulse()
.
The notified thread will be able to acquire the lock after the throbbing thread releases the lock (there may already be others in the queue).
From MSDN, Monitor.Pulse () :
"The thread that currently owns the lock for the specified object calls this method to signal the next thread on the line about the lock. After receiving a pulse, the waiting thread is moved to the ready queue. When the thread that called Pulse , releases the lock The next thread in the ready queue (which is not necessarily the thread that was pulse) acquires the lock. "
"Will only the thread waiting on line C block access? Or will it compete with all other threads waiting on line A?"
All threads in the ready queue "compete" for the next lock.
It doesn't matter if they came directly or from the waiting queue with help Pulse()
.
Queues can be implemented in other ways.(Not a queue data structure per se).
Thus, the implementation Monitor
may not guarantee fairness, but may have higher overall throughput / performance.
a source to share