Monitor.Wait/Monitor.Pulse

  • Thread starter Thread starter ozbear
  • Start date Start date
O

ozbear

I just want to be clear on this....

If a threadA performs a Monitor.Pulse(lockobject),
but threadB has not yet entered Monitor.Wait(lockobject), when threadB
finally gets around to doing its Monitor.Wait(lockobject) it is
possible that it will wait forever if not pulsed again, yes?

The description of how Wait works strongly imply this since the
Pulse only readies threads in the lockobject's waitlist and since
threadB isn't on it (yet) it will completely miss the pulse.

This isn't explicitely stated anywhere I can find.

Is my understanding correct?

Oz
 
You're right.

But don't forget that the call to Pulse must be inside a lock
(Monitor.Enter) of the object the Wait call releases. This way it will never
wait forever.

The following example is a correct use of Wait/Pulse, with comments that
explain it:

private sometype threadSharedResource; // A thread-shared resource.

public void Method1()
{
// We lock the shared resource.
lock (threadSharedResource)
{
...
// We need to wait for the state of the resource to be changed.
// And the state will be changed by other thread/s,
// so we unlock, temporarily, the resource.
Monitor.Wait(threadSharedResource);
}
}

public void Method2()
{
// We lock the shared resource.
lock (threadSharedResource)
{
// We do work with the resource…

…

// And now we do an operation that changes the state of the resource.

// We know that maybe there are other threads waiting for the state
of the
// resource to change, so we pulse the lock we have, just in case
there
// were threads waiting, as I said.
Monitor.PulseAll(threadSharedResource);
// Depending on what you are doing, in some situations
// you could want to remove from the waiting-threads list
// only the one from the top of the list, in that case use Pulse.

// After the Pulse, the threads are moved from the waiting-list
// to the “ready-to-get-the-lock†list (I don’t remember the exact
// name at this time).

threadSharedResource.OperationThatChangesState(); // The operation.
} // Finally we leave the lock.
}

If this behavior doesn't like you, maybe Wait/Pulse is not what you're
looking for.

If the resource has more than one state you can do “sync objectsâ€

private object someStateSyncObj = new object();

…and lock them.

Now, a real example with a collection (it is said that a collection has only
one state, which is the contents of the collection itself):

private ArrayList arrayList = new ArrayList();

public void Method1() // Thread-safe method.
{
lock (arrayList)
{
// Imagine we need at least two items.
while (arrayList.Count < 2)
Monitor.Wait(arrayList);

// Do work with the items…
}
}

public void AddItem(object item) // Thread-safe method.
{
lock (arrayList)
{
Monitor.PulseAll(arrayList); // We’re gonna change the state.
// We could also do Pulse.

arrayList.Add(item);
}
}

public void RemoveItem(int index) // Thread-safe method.
{
lock (arrayList)
{
Monitor.Pulse(arrayList);
arrayList.RemoveAt(index);
}
}

I hope this was helpful.

And sorry for my bad english, I’m Spanish.
 
You're right.

But don't forget that the call to Pulse must be inside a lock
(Monitor.Enter) of the object the Wait call releases. This way it will never
wait forever.
<snip>

I am aware that Pulse/Wait have to be in a preceding Monitor.Enter.
It was the thread doing the Wait that can wait forever if it misses
the pulse.

In my case, ThreadB had to release all of its locks because it
had a usercallback to call and holding locks while performing a
user call back function is generally a certain way to a deadlock.

I have a workaround that doesn't break any rules.


Oz
 
Back
Top