【问题标题】:Background Worker events后台工作者事件
【发布时间】:2011-01-02 10:54:45
【问题描述】:

伪代码:

form1
{
    int i;
    label1;

    Add()
    {
     i++;
     label1 = i.ToString(); //#ErrorLine
    }

    backgroundworker worker;

    worker_DoWork()
    { 
     FileGuard guard = new FileGuard();
     guard.FileKilled += guard.KillH(Add);
     guard.StarGuarding(); //there is system watcher inside 
                           //this guard and some processing code
                           //that will fire event FileKilled();

    } 
} 

调用 StartGuarding() worker 后将完成 但是当触发 FileKilled 事件时,我在 #ErrorLine

行出现了这个错误

跨线程操作无效:控件“form1”从创建它的线程以外的线程访问。

【问题讨论】:

    标签: c# .net multithreading event-handling backgroundworker


    【解决方案1】:

    这与事件本身无关,而是您正在从另一个线程访问 UI 控件这一事实。在 Windows 窗体中,除了主 UI 线程之外,不允许您从任何其他线程与 UI 交互。

    您可以使用InvokeRequired 来检查您是否在一个无法访问UI 的线程上,然后在需要时使用Invoke 在UI 线程上运行代码。它可能看起来像这样:

    private void DoStuffWithGUI() 
    {
        if (InvokeRequired) 
        {
            Action work = DoStuffWithGUI;
            Invoke(work);
        }
        else
        {
            // Your normal logic
        }
    }
    

    您可以直接从 ProgressChanged 和 RunWorkerCompletedEvents 使用 UI(因为它们会自动编组到 UI 线程)。但是您在 DoWork 中所做的所有工作(以及您可能在工作中引发的所有事件)都在单独的线程中运行,并且必须使用 Invoke 编组到 UI 线程。来自 MSDN 的 BackgroundWorker

    你必须小心不要操纵 您的任何用户界面对象 DoWork 事件处理程序。反而, 与用户界面通信 通过 ProgressChanged 和 RunWorkerCompleted 事件。

    【讨论】:

    • BackgroundWorker 不允许更改 UI 吗?我是这么想的。
    • 不,BackgroundWorker 运行在与主 UI 不同的线程上,这是 BackgroundWorker 的主要目的。
    • 好的。我的印象是它具有一些神奇的同步功能,可以处理 GUI 更新。但是我只是在MSDN中找到了这段文字:You must be careful not to manipulate any user-interface objects in your DoWork event handler. Instead, communicate to the user interface through the ProgressChanged and RunWorkerCompleted events来源:msdn.microsoft.com/en-us/library/…
    【解决方案2】:

    这是因为系统调用 FileKilled 时使用了第三个线程。

    至于BackgroundWorker,您应该使用事件来处理 GUI 更新:http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx

    【讨论】:

      【解决方案3】:

      您无法从创建它们的表单以外的任何其他地方访问 Windows 窗体或 WPF 对象,因此您的问题。

      使用调度程序将更新发送回您的 UI 线程。

      如果您可以详细说明您使用的是 WinForms 还是 WPF,我们可以提供更多信息。

      【讨论】:

        【解决方案4】:

        您不能从创建控件的线程以外的线程更改修改控件。您需要使用InvokeRequired 属性和Invoke 方法来编组从后台线程对UI 线程的调用。

        private readonly _lockObject = new Object();
        
        Add()
        {
             lock(_lockObject)
             {
                 i++;
                 if(label1.InvokeRequired)
                     Invoke(new Action( () => label1 = i.ToString()));
                 else
                     label1 = i.ToString();
             }
        }
        

        请注意,lock 不是避免此异常所必需的。添加它是为了使方法线程安全。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-07-29
          • 1970-01-01
          • 2011-10-30
          • 2011-03-17
          • 1970-01-01
          相关资源
          最近更新 更多