尝试从另一个线程访问复杂对象时出现InvalidOperationException

时间:2014-06-01 00:29:07

标签: c# wpf multithreading backgroundworker

在我尝试了很多很多解决方案之后,我无论如何都无法解决这个问题所以我开始相信这个问题没有解决办法。

我有一个包含复杂属性的对象。例如:List<SomeComplexObject>。我在工作线程上运行此类的方法,以保持GUI运行,直到工作线程完成。当它完成执行时,我想使用这些对象的属性来更新GUI,假设我想使用List<SomeComplexObject>循环遍历此列表并更新GUI。但每次我尝试访问此列表时,调试器都会抛出InvalidOperationException:调用线程无法访问此对象,因为另一个线程拥有它。

我试图让这个类的所有属性都变化,但没有希望我也使用Lazy<T>类方法来解决但是同样的问题也出现了。

包含worker函数的类:

public class MainModules
{

    #region Attributes

    public VIDEO video; 

    public string VideoPath
    {
        get;
        set;
    }

    LowLevelModule lowLevelOutput;

    //this list that I want to use to Update GUI 
    public volatile List<FaceRecognitionModule> faceModuleOutput;

    //worker function running on different thread
     public void RunMainModules()
     {
        //some complex work to set the class attributes
     }
 }

GUI类中的线程创建

 private void RunMainModules_BtnClick(object sender, RoutedEventArgs e)
    {
      //  MainModule = new MainModules(mainModuleObj, Inpath, lif, keyframefolderpath, trdbpath, labelspath, rrankspath, alignmatpath, 11, 10);
        this.LazyMainModule = new Lazy<MainModules>(this.InitLazyMainModule);
        MainModuleThread = new Thread(this.RunMainModules);
        MainModuleThread.Start(MainModule);

    }

    public MainModules InitLazyMainModule()
    {
        return new MainModules(mainModuleObj, Inpath, lif, keyframefolderpath, trdbpath, labelspath, rrankspath, alignmatpath, 11, 10);
    }
     public void RunMainModules(Object obj)
    {
        //MainModules mm = obj as MainModules;
        MainModules mm = LazyMainModule.Value;
        mm.RunMainModules();
        this.Dispatcher.Invoke((Action)(() =>
        {
            this.InitSpeechRec_Btn.IsEnabled = true;
        }));
    }

当我尝试从GUI faceModuleOutput访问MainModules时,我获得了InvalidOperationException

Image img = new Image();
//InvalidOperationException occurs here
img.Source = LazyMainModule.Value.faceModuleOutput[0].keyframes[1].keyframe;

简要介绍这篇文章: 我想从主线程访问由后台线程实例化的对象,但它会抛出

InvalidOperationException : The calling thread cannot access this object because a different thread owns it. 

3 个答案:

答案 0 :(得分:0)

需要从GUI线程创建/修改UI控件。否则是非法的。

似乎MainModuleThread(至少)正在创建和修改Image。这应该在GUI Thread(调用RunMainModules_BtnClick

中完成

答案 1 :(得分:0)

您无法修改甚至访问与其他线程的UI线程相关的任何内容。这有时会变得非常极端/烦人,因为你甚至无法在文本框中获取值或检查是否选中了复选框。如果要对UI线程拥有的对象执行操作,则需要调用UI线程来执行操作。

UIObject.Dispatcher.Invoke(() => {
   //[Perform your action in here]
  });

答案 2 :(得分:0)

最后我找到了解决方案......类BitmapImage是线程仿射的,所以它不能被多个线程访问,你需要先打开它才能读取只是为了写入而关闭所以编译器可以保证没有线程会修改它的内容

所以解决方案......:

 //keyframe here is a BitmapImage so on creation we must call keyframe.Freeze()
 LazyMainModule.Value.faceModuleOutput[0].keyframes[1].keyframe;

班级KeyFrame

public class KeyFrame
{
    public volatile BitmapImage keyframe;
    public volatile List<string> personsNames;
    public volatile List<string> categories;

    public KeyFrame(BitmapImage keyframe, List<string> personsNames, List<string> categories)
    {
        this.keyframe = keyframe;
        //here we call Freeze funcition on creation to make it modifiable 
        this.keyframe.Freeze();
        this.personsNames = personsNames;
        this.categories = categories;
    }
}