【问题标题】:Cross-thread operation not valid, even if using InvokeRequired [duplicate]跨线程操作无效,即使使用 InvokeRequired [重复]
【发布时间】:2010-12-16 17:58:48
【问题描述】:

我有一个表单,上面有我的自定义控件。

我的表单中有一个方法:

private void SetEnabledOnControls(bool val)
{
  if (InvokeRequired)
  {
      Invoke((Action<bool>)SetEnabledOnControls, val);
  }
  else
  {
       //do the work - iterate over child controls, 
       //and they iterate over their children, etc...
  }
}

else 分支上的方法中,我得到了提到的异常:
Cross-thread operation not valid: Control 'txtNumber' accessed from a thread other than the thread it was created on.

我的场景实际上有点复杂 - 我只是将其作为示例进行推断。实际发生的情况是我正在使用 WorkflowFoundation - 我在 WorkflowApplication 中运行 StateMachineActivity (CTP1)(它在它自己的线程中运行),我订阅了它的事件,然后我从那里调用 SetEnabledOnControls。此外,我正在使用书签来恢复我的工作流程(另外,还有 MEF,不参与场景)。

所有这些都与我对 InvokeRequired 的明显误解无关 - 如果 InvokeRequired 为假,我怎么可能有跨线程异常?我没有“手动”创建任何控件——它们都在设计师放置的 Initialize() 中。

有人能解释一下吗?

谢谢!

编辑 使用 GWLlosa 建议,我使用System.Threading.Thread.CurrentThread.ManagedThreadId 跟踪了 ThreadId。现在来了奇怪的部分...... Initialize() 中的线程 id 是 10。在传递前 2 个状态之间,它的 ID 为 13 - InvokeRequired 为真,它被正确调用。但是,在第二个状态之后,当它进入SetEnabledOnControls 时又是 13,但是这次 InvokeRequired 是假的!怎么会!?后来,当然,它无法更改子控件(不足为奇)。会不会是表单以某种方式改变了它所在的线程??

编辑 2 现在我打电话给:

 if (IsHandleCreated)
 {
     Invoke((Action<bool>)SetEnabledOnControls, val);
 }

它的IsHandleCreated 为真,但仍然以 devSpeed pointed at 失败。

编辑 3 FACEPALM :) 恢复状态的按钮之一最初是表单的 CancelButton。当它从属性中删除时,codebihind 仍然有 DialogResult=Cancel ——所以我的表单确实正在关闭,当然它缺少句柄,所以 InvokeRequired 没有返回正确的信息,因此出现错误.

谢谢大家!我今天学到了一个新东西:)

【问题讨论】:

    标签: c# winforms .net-4.0 thread-safety multithreading


    【解决方案1】:

    如果您在创建控件时(在 Initialize() 函数中)记录线程 ID 并在尝试触摸它之前记录线程 ID,这可能会使您的调试更容易。通常,当您以某种方式在线程上创建控件而不是您最初期望的线程时,我已经看到这种情况发生。

    【讨论】:

    • 非常有帮助!查看我的更新结果...
    【解决方案2】:

    我自己也遇到了类似的问题,我有一个函数被拆除,然后在 FlowLayoutPanel 中动态创建控件:

            public static void RenderEditorInstance(DataContext dataContext, object selectedItem, Form targetForm, Control targetControl, List<DynamicUserInterface.EditorControl> editorControls, EventHandler ComboBox_SelectedIndexChanged, EventHandler TextBoxControl_TextChanged, EventHandler CheckBox_CheckChanged, EventHandler NumericUpDown_ValueChanged, CheckedListControl.ItemChecked OnItemChecked, EventHandler dateTimePicker_ValueChanged, DynamicUserInterface.DuplicationValidationFailed liveLookupValidationFailed, DynamicUserInterface.PopulateComboBoxCallback populateComboBoxCallback)
            {           if (targetForm.InvokeRequired)
                {
                    InstanceRenderer renderer = new InstanceRenderer(RenderEditorInstance);
                    targetForm.Invoke(renderer, dataContext, selectedItem, targetForm, targetControl, editorControls, ComboBox_SelectedIndexChanged, TextBoxControl_TextChanged, CheckBox_CheckChanged, NumericUpDown_ValueChanged, OnItemChecked, dateTimePicker_ValueChanged, liveLookupValidationFailed, populateComboBoxCallback);
                }
                else
                {
                    targetControl.Padding = new Padding(2);
                    targetControl.Controls.Clear();
    
                    ...{other code doing stuff here }
                }
             }
    

    在大约 12 个使用此代码的实例中,引发了跨线程异常。使用此代码的所有实例都以这样的方式编写,即使用“await”关键字异步实现界面构建。

    根据GWLlosa的建议,我写了一个控件的扩展方法来获取一个控件所属的OwningThread:

        public static Thread OwnerThread(this Control ctrl)
        {
            Thread activeThread = null;
    
            if (ctrl.InvokeRequired)
            {
                activeThread = (Thread)ctrl.Invoke(new Func<Control, Thread>(OwnerThread), new object[] { ctrl });
            }
            else
            {
                activeThread = Thread.CurrentThread;
            }
    
            return activeThread;
        }
    

    ..它强调了经过几次迭代后,线程 ID 确实发生了变化。

    这些代码的深处隐藏着用于获取填充相关控件的数据的例程,通过使用来自 MSDN (https://msdn.microsoft.com/en-us/library/hh195051(v=vs.110).aspx) 的 Task.Run() 明确指出:

    该示例显示异步任务在不同的 线程比主应用程序线程

    一旦 Task.Run() 被排除在等式之外,控件的线程就永远不会改变。所以你需要注意如何以及何时使用它!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-08-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-12-04
      • 1970-01-01
      相关资源
      最近更新 更多