我有一个设想。(Windows Forms, c#, .NET)

There is a main form which hosts some user control. The user control does some heavy data operation, such that if I directly call the UserControl_Load method the UI become nonresponsive for the duration for load method execution. To overcome this I load data on different thread (trying to change existing code as little as I can) I used a background worker thread which will be loading the data and when done will notify the application that it has done its work. Now came a real problem. All the UI (main form and its child usercontrols) was created on the primary main thread. In the LOAD method of the usercontrol I'm fetching data based on the values of some control (like textbox) on userControl.

伪代码看起来像这样:

代码1

UserContrl1_LoadDataMethod()
{
    if (textbox1.text == "MyName") // This gives exception
    {
        //Load data corresponding to "MyName".
        //Populate a globale variable List<string> which will be binded to grid at some later stage.
    }
}

它给出的例外是

跨线程操作无效:从创建该控件的线程以外的线程访问的控件。

为了了解更多关于这一点,我做了一些谷歌搜索,并提出了一个建议,如使用以下代码

代码2

UserContrl1_LoadDataMethod()
{
    if (InvokeRequired) // Line #1
    {
        this.Invoke(new MethodInvoker(UserContrl1_LoadDataMethod));
        return;
    }

    if (textbox1.text == "MyName") // Now it won't give an exception
    {
    //Load data correspondin to "MyName"
        //Populate a globale variable List<string> which will be binded to grid at some later stage
    }
}

但我似乎还是回到了原点。再次申请 变得反应迟钝。这似乎是由于第1行if条件的执行。加载任务再次由父线程完成,而不是由我生成的第三个线程完成。

我不知道我认为这是对还是错。

我如何解决这个问题,以及执行第1行if块的效果是什么?

情况是这样的:我想根据控件的值将数据加载到全局变量中。我不想改变子线程中控件的值。我不会从子线程中做。

因此只有访问值,才能从数据库中获取相应的数据。


当前回答

跨线程操作有两个选项。

Control.InvokeRequired Property 

第二个是使用

SynchronizationContext Post Method

控制。InvokeRequired只在从Control类继承控件时有用,而SynchronizationContext可以在任何地方使用。一些有用的信息如下链接

跨线程更新UI | .Net

使用SynchronizationContext | .Net进行跨线程更新UI

其他回答

你需要看看Backgroundworker的例子: http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx 特别是它如何与UI层交互。根据你的帖子,这似乎回答了你的问题。

这不是解决此错误的推荐方法,但您可以快速抑制它,它将完成工作。对于原型或演示,我更喜欢这样。添加

CheckForIllegalCrossThreadCalls = false

在Form1()构造函数中。

简单地使用这个:

this.Invoke((MethodInvoker)delegate
            {
                YourControl.Property= value; // runs thread safe
            });

UI跨线程问题最干净(也是最合适)的解决方案是使用SynchronizationContext,请参见多线程应用程序中同步对UI的调用的文章,它很好地解释了这一点。

. net中的控件通常不是线程安全的。这意味着您不应该从其他线程访问控件,而不是它所在的线程。为了解决这个问题,您需要调用控件,这就是第二个示例所尝试的。

然而,在您的例子中,您所做的只是将长时间运行的方法传递回主线程。当然,这并不是你真正想做的。你需要重新思考这一点,以便你在主线程上所做的一切都是在这里和那里设置一个快速属性。