C# 跨线程操作无效,即使使用InvokeRequired

C# 跨线程操作无效,即使使用InvokeRequired,c#,winforms,.net-4.0,thread-safety,multithreading,C#,Winforms,.net 4.0,Thread Safety,Multithreading,我有一个窗体,上面有我的自定义控件 我的表格中有一个方法: private void SetEnabledOnControls(bool val) { if (InvokeRequired) { Invoke((Action<bool>)SetEnabledOnControls, val); } else { //do the work - iterate over child controls, //and they i

我有一个窗体,上面有我的自定义控件

我的表格中有一个方法:

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...
  }
}
它已将
IsHandleCreated
设置为true,但仍然以devSpeed的方式失败

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


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

如果在创建控件时(在Initialize()函数中)记录线程ID,并在尝试触摸它之前记录线程ID,那么调试可能会更容易。一般来说,当您在一个线程上创建控件而不是在一开始期望的线程上创建控件时,我会看到这种情况发生。

我自己也遇到了类似的问题,我有一个函数正在分解,然后在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 }
            }
         }
publicstaticvoidrenderditioninstance(DataContext DataContext、object selectedItem、Form targetForm、Control targetControl、List Editor控件、EventHandler ComboBox\u SelectedIndexChanged、EventHandler TextBoxControl\u TextChanged、EventHandler CheckBox\u CheckChanged、EventHandler NumericUpDown\u ValueChanged、CheckedListControl.ItemChecked OnItem检查、EventHandler dateTimePicker\u值已更改,DynamicUserInterface.ReplicationValidationFailed liveLookupValidationFailed,DynamicUserInterface.PopulateComboxCallback PopulateComboxCallback)
{if(targetForm.invokererequired)
{
InstanceRenderer渲染器=新InstanceRenderer(渲染器实例);
调用(渲染器、dataContext、selectedItem、targetForm、targetControl、EditorControl、ComboBox\u SelectedIndexChanged、TextBoxControl\u TextChanged、CheckBox\u CheckChanged、NumericUpDown\u ValueChanged、OnItemChecked、dateTimePicker\u ValueChanged、liveLookupValidationFailed、PopulateComboxCallback);
}
其他的
{
填充=新填充(2);
targetControl.Controls.Clear();
…{其他代码在这里做东西}
}
}
在使用此代码的大约12个实例中,有一个实例引发了跨线程异常。使用此代码的所有实例都是以这样一种方式编写的,即使用“wait”关键字异步实现接口构建

根据Gwlosa的建议,我为控件编写了一个扩展方法,以获取控件所属的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;
    }
公共静态线程所有者线程(此控件ctrl)
{
线程activeThread=null;
如果(ctrl.InvokeRequired)
{
activeThread=(Thread)ctrl.Invoke(newfunc(OwnerThread),newobject[]{ctrl});
}
其他的
{
activeThread=Thread.CurrentThread;
}
返回活动线程;
}
…这突出表明,经过几次迭代后,线程Id确实发生了变化

在这些代码的深处,有一些例程通过使用Task.Run()来获取填充相关控件的数据,MSDN()中明确指出:

该示例显示异步任务以不同的方式执行 线程,而不是主应用程序线程


一旦Task.Run()从等式中删除,控件的线程就不会更改。因此,您需要注意如何以及何时使用它!

非常有用!有关结果,请参阅我的更新。。。
    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;
    }