代码之家  ›  专栏  ›  技术社区  ›  Juk

背景工作者问题

  •  1
  • Juk  · 技术社区  · 15 年前

    我有UI WPF应用程序。

    也许有人知道为什么这个代码不起作用?

    案例1:

    BackgroundWorker worker = new BackgroundWorker();
    worker.DoWork += delegate
    {
       //some logic
    };
    
    worker.RunWorkerAsync();
    

    如果那样的话,我会得到例外 调用线程无法访问此对象,因为其他线程拥有它。 然后我把它改成:

    BackgroundWorker worker = new BackgroundWorker();
    worker.DoWork += delegate 
    { 
      this.Dispatcher.BeginInvoke(
        new Action(() =>  { //my code here }), null); 
    }; 
    

    在执行此代码期间,用户界面冻结之后。就像是在同一个线程中执行

    案例2:

    BackgroundWorker worker = new BackgroundWorker();
    worker.RunWorkerAsync(new Action(() =>
    {
     //some code here
    }));
    

    在这种情况下,不会执行操作内部的代码。

    //————————————————————————————————————————————————————————————————————————————————————————————————————————————————————-//
    谢谢你们的帮助 下面提到了我的代码不能正常工作的原因。我访问了后台线程中的一些UI元素。现在,在调用BackgroundWorker之前,我从UI元素中获取所有值。现在,我声明新的变量,为它们分配来自必需的UI元素的所有值,然后将这些变量传递给backgroundworker,而不是ui元素(它最初是这样做的)。

    3 回复  |  直到 12 年前
        1
  •  1
  •   Nir    15 年前

    第一个版本的问题是,在“某些逻辑”中,您可能正在访问WPF对象——不能这样做,只能从创建它们的同一线程访问WPF对象。

    第二个版本的问题是,您正在启动一个后台线程,该线程要求主线程完成所有工作,然后退出,所以您正在主线程(也完成所有UI工作)上完成所有工作,并且UI冻结,本质上这相当于根本不使用BackgroundWorker。

    正如乔恩·斯基特所说,第三个版本是简单的错误用法,不应该像你想象的那样工作。

    那么,你需要做什么?

    在启动后台工作程序之前,需要从主线程的UI中收集所有信息,只能使用简单类型(string、int、double等)和线程安全类/结构,不能在后台工作程序执行的代码中使用任何WPF类。

    收集完所有可以调用runworkerasync的数据后,在DoWork处理程序中,您不能从UI读取数据-只能访问以前准备的数据,也不能写入UI-必须将其保存到其他地方(例如类成员),并在BackgroundWorker完成后将其复制到UI。

    “无法从另一个线程访问WPF”规则的唯一例外是Freezable(以及从Freezable继承的所有类),在调用Freeze方法之后,这使得对象为只读且线程安全。

        2
  •  1
  •   Jon Skeet    15 年前

    第二个版本不会做您想要的,因为参数的重载 RunWorkerAsync 它接受一个参数,只是用来作为 DoWorkEventArgs.Argument . 它并不意味着要执行的操作-除非您提供一个事件处理程序,将值强制转换为 Action 并称之为…

    第一个版本应该有效-但是根据Oded的评论,你没有给出任何你所说的“不工作”的迹象…您也没有指定两个版本都失败还是只有一个。

        3
  •  0
  •   Robert Rossney    15 年前

    另一件事要非常小心:如果你正在设置 DoWork 对于匿名方法,请确保不在调用方法中的对象上创建闭包。这些对象位于调用方法的线程上,如果 道沃克 方法会碰到它们,这是一件坏事。

    一个简单的例子:

    MyClass foo = new MyClass();
    worker.DoWork += delegate
    {
       foo.MyProperty++;
    };
    

    一般来说,我很少使用匿名方法。如果不是有意使用闭包,闭包会产生各种各样的问题。如果你是故意使用它们,那么很容易写下一些代码,这些代码太微妙了,根本无法修改一年。用显式参数编写一个命名方法可能需要更多的时间和代码,但这并不比用一个闭包来记录您正在做什么困难。

    我不使用匿名方法 完全 BackgroundWorker . 不仅存在这样的风险,即您将依赖于一个闭包而不真正考虑它的所有含义,而且您还很可能编写了无法进行单元测试的代码。

    推荐文章