【问题标题】:Thread Pooling: Cross-thread operation not valid.线程池:跨线程操作无效。
【发布时间】:2011-12-29 16:00:29
【问题描述】:

在线程方面我还是个新手,但是在使用以下代码时我得到了InvalidOperationException。我知道它正在尝试访问importFileGridView,但这是由创建异常的 UI 线程创建的。我的问题是,我该如何解决这个问题? GetAllImports 是否有可能具有返回类型?如何从我的 UI 线程访问 temp

ThreadPool.QueueUserWorkItem(new WaitCallback(GetAllImports), null);

private void GetAllImports(object x)
    {
        DataSet temp = EngineBllUtility.GetAllImportFiles(connectionString);
        if (temp != null)
            importFileGridView.DataSource = temp.Tables[0];
        else
            MessageBox.Show("There were no results. Please try a different search", "Unsuccessful", MessageBoxButtons.OK, MessageBoxIcon.Information);
    }

【问题讨论】:

    标签: c# .net multithreading threadpool


    【解决方案1】:

    您不能在后台线程上更改用户界面组件。在这种情况下,必须在 UI 线程上设置 DataSource。

    您可以通过Control.InvokeControl.BeginInvoke 处理此问题,如下所示:

    private void GetAllImports(object x)
    {
        DataSet temp = EngineBllUtility.GetAllImportFiles(connectionString);
        if (temp != null)
        {
            // Use Control.Invoke to push this onto the UI thread
            importFileGridView.Invoke((Action) 
                () => 
                {
                    importFileGridView.DataSource = temp.Tables[0];
                });
        }
        else
            MessageBox.Show("There were no results. Please try a different search", "Unsuccessful", MessageBoxButtons.OK, MessageBoxIcon.Information);
    }
    

    【讨论】:

    • 通常当我看到这段代码时,它通常会在执行之前检查是否确实需要调用(使用.InvokeRequired)。这种情况是否有什么特别之处使得它变得不必要?
    • @M.Babcock 因为这是直接在来自ThreadPool.QueueUserWorkItem 的调用中完成的,所以您提前知道您在线程池线程上。这意味着 InvokeRequired 将始终返回 true。进行检查成为不必要的检查(在这种特定情况下)。但是,如果您正在编写一个可以在 UI 线程和其他线程中使用的方法,那么添加检查是个好主意。
    • 感谢您的澄清。
    • @ReedCopsey 见谅,但我不明白如何实现这个?
    • @user1016253 你可以用上面的代码替换你的代码,它应该可以解决问题...
    【解决方案2】:

    reed 说了什么,但我更喜欢这种语法:

    发生的情况是您正在创建一个委托函数,该函数将作为参数通过调用它的Control.Invoke 传递给 UI 线程,这样 UI 线程就会对 importFileGridView 进行更改。

    importFileGridView.Invoke((MethodInvoker) delegate {
                                 importFileGridView.DataSource = temp.Tables[0];
                             });
    

    你也可以这样写:

    //create a delegate with the function signature
    public delegate void SetDateSourceForGridViewDelegate (GridView gridView, Object dataSource);
    
    //write a function that will change the ui
    public void SetDataSourceForGridView(GridView gridView, Object dataSource)
    {
        gridView.DataSource = dataSource;
    }
    
    //Create a variable that will hold the function
    SetDateSourceForGridViewDelegate delegateToInvoke = SetDataSourceForGridView;
    
    //tell the ui to invoke the method stored in the value with the given paramters.
    importFileGridView.Invoke(delegateToInvoke, importFileGridView, temp.Tables[0]);
    

    我建议使用MethodInvoker 而不是Action 请参阅:here

    【讨论】:

      最近更新 更多