简体   繁体   中英

"Object synchronization method was called from an unsynchronized block of code" error in Callback test

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 and Monitor.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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM