Home > Net >  "Object synchronization method was called from an unsynchronized block of code" error in C
"Object synchronization method was called from an unsynchronized block of code" error in C

Time:06-28

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?

CodePudding user response:

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).

  • Related