The following class is supposed to test, if an expected number of asynchronous Callbacks was received:
class Program
{
static List<int> callbackObjects = new List<int>();
static int expectedNumberOfCallbacks;
static void Main(string[] args)
{
expectedNumberOfCallbacks = 10;
Monitor.Enter(callbackObjects);
CallbackTest.CallbackTester.TriggerCallbacks(expectedNumberOfCallbacks,Callback);
Monitor.Wait(callbackObjects,10000);
Assert.AreEqual(callbackObjects.Count, expectedNumberOfCallbacks);
}
private static void Callback(int callbackObject)
{
callbackObjects.Add(callbackObject);
Console.WriteLine($"Callback number {callbackObjects.Count}");
if(callbackObjects.Count == expectedNumberOfCallbacks)
{
Monitor.Exit(callbackObjects);
}
}
}
The code outputs
Callback number 1
Callback number 2
Callback number 3
Callback number 4
Callback number 5
Callback number 6
Callback number 7
Callback number 8
Callback number 9
Callback number 10
but then it throws an
System.Threading.SynchronizationLockException: 'Object synchronization method was called from an unsynchronized block
on executing
Monitor.Exit(callbackObjects);
Is it because Monitor.Enter
and Monitor.Exit
are invoked in different scopes/threads?
How can I rewrite the code, to do the
Assert.AreEqual(callbackObjects.Count, expectedNumberOfCallbacks)
test in Main
, either after the expected number of callbacks was received or a predefined timeout?
Is it because
Monitor.Enter
andMonitor.Exit
are invoked in different scopes/threads?
Yes, exactly that. Monitor
is thread-bound.
However, IMO you should also consider whether the multiple callbacks can themselves be overlapped; if they are, you need to synchronize inside the callback so that callbackObjects.Add
is well-defined. It could be that what you really need here is lock
and Pulse
:
static void Main(string[] args)
{
expectedNumberOfCallbacks = 10;
lock (callbackObjects)
{
CallbackTest.CallbackTester.TriggerCallbacks(
expectedNumberOfCallbacks,Callback);
Monitor.Wait(callbackObjects, 10000);
Assert.AreEqual(callbackObjects.Count, expectedNumberOfCallbacks);
}
}
private static void Callback(int callbackObject)
{
lock (callbackObjects)
{
callbackObjects.Add(callbackObject);
Console.WriteLine($"Callback number {callbackObjects.Count}");
if(callbackObjects.Count == expectedNumberOfCallbacks)
{
Monitor.Pulse(callbackObjects);
}
}
}
Wait
releases a monitor and waits for either a signal or a timeout to re-acquire it; Pulse
and PulseAll
are what sends that signal to a waiting thread (or threads).
You can also use the return value from Wait
to determine whether or not you got signaled (vs timeout).
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.