Search code examples
c#.netwpfthread-synchronization

Can't access object on main thread


So my application does as follows:

Main Thread:

  1. Create new object1, which starts Thread1 in its constructor.
  2. Instantiate object2, that contains a Vector3D and a AxisAngleRotation3D (among other things)
  3. Do things
  4. Thread1 tries to access object2.Position (Vector3D) and object2.Orientation (AxisAngleRotation3D). Position is OK, Orientation.Angle throws 'System.InvalidOperationException' saying "Calling thread cannot access the object because another thread owns it".

Both properties are created in the main thread, and it's only the AxisAngleRotation3D.Angle, Axis and InternalQuaternion that give the error. Debugging, I can confirm that the AxisAngleRotation3D object is created in the MainThread, and can't find any place on my code where it's accessed again, I've commented the only line where it's changed, so the only thing it does is be instantiated.

I'm totally lost here. Shouldn't I be able to access objects created in the parent thread? It works for other threads that I create similarly.

Is there any way I can see which thread owns the object that is throwing the exception, or debug when the AxisAngleRotation3D properties are being changed outside my own code?

I open the threads with ThreadPool.QueueUserWorkItem

Thanks


Solution

  • The problem looks like it's already stated:

    • Objects created on main thread (Thread 0)
    • Tried to access object from Thread 1: Threw InvalidOperationException

    The bottom line is that if you need cross thread access to UI objects you have a couple options:

    • If the object is Freezable and you aren't going to change it's values you can freeze it. (Vectors and Rotations are Freezable)
    • Otherwise you can use the dependency object's dispatcher to access the values

    Freezing Objects

    if(freezable.CanFreeze)
    {
        freezable.Freeze();
    }
    

    At this point you can read the objects on any thread--but you can't edit them again. You can get an unfrozen copy of the class by calling the Clone() method and manipulate that, but you can't change values on the frozen object.

    Using the Dispatcher

    Freezable objects are also DependencyObjects so this works for anything in the UI layer:

    var myAngle = myRotation.Dispatcher.CheckAccess() 
        ? myRotation.Orientation.Angle
        : (double)myRotation.Dispatcher.Invoke(() => myRotation.Orientation.Angle);
    

    Of course, if the thread you are on is the UI thread (Dispatcher.CheckAccess() == true) then you don't need to worry about accessing the property. If it is any other thread (Dispatcher.CheckAccess() == false), then you have to pause this thread, queue work on the dispatcher and return when it is done.