跨线程操作无效:从线程以外的线程访问控制。

时间:2022-01-11 10:58:10

I have a scenario. (Windows Forms, C#, .NET)

我有一个场景。(Windows窗体,c#,. net)

  1. There is a main form which hosts some user control.
  2. 有一个主窗体承载一些用户控件。
  3. 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.
  4. 用户控件执行一些繁重的数据操作,例如,如果我直接调用UserControl_Load方法,UI就会在加载方法执行期间变得不响应。
  5. To overcome this I load data on different thread (trying to change existing code as little as I can)
  6. 为了克服这个问题,我在不同的线程上加载数据(尝试尽可能少地更改现有代码)
  7. 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.
  8. 我使用了一个后台工作线程,它将加载数据,当完成时将通知应用程序它已经完成了它的工作。
  9. 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.
  10. 现在出现了一个真正的问题。所有UI(主窗体和它的子用户控件)都是在主主线程上创建的。在usercontrol的LOAD方法中,我根据用户控件的某些控件的值(比如文本框)获取数据。

The pseudocode would look like this:

伪代码如下所示:

CODE 1

代码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.
    }
}

The Exception it gave was

它给出的例外是。

Cross-thread operation not valid: Control accessed from a thread other than the thread it was created on.

跨线程操作无效:从线程以外的线程访问控制。

To know more about this I did some googling and a suggestion came up like using the following code

为了进一步了解这一点,我做了一些谷歌搜索,一个建议就像使用下面的代码一样。

CODE 2

代码2

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

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

BUT BUT BUT... it seems I'm back to square one. The Application again become nonresponsive. It seems to be due to the execution of line #1 if condition. The loading task is again done by the parent thread and not the third that I spawned.

但是但是…看来我又回到起点了。应用程序再次变得无响应。这似乎是由于第1行的执行情况。加载任务再次由父线程完成,而不是我生成的第三个线程。

I don't know whether I perceived this right or wrong. I'm new to threading.

我不知道我是否意识到这是对还是错。我新线程。

How do I resolve this and also what is the effect of execution of Line#1 if block?

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

The situation is this: I want to load data into a global variable based on the value of a control. I don't want to change the value of a control from the child thread. I'm not going to do it ever from a child thread.

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

So only accessing the value so that the corresponding data can be fetched from the database.

因此只访问值,以便从数据库获取相应的数据。

20 个解决方案

#1


352  

As per Prerak K's update comment (since deleted):

根据preak K的更新评论(自删除后):

I guess I have not presented the question properly.

我想我没有恰当地提出这个问题。

Situation is this: I want to load data into a global variable based on the value of a control. I don't want to change the value of a control from the child thread. I'm not going to do it ever from a child thread.

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

So only accessing the value so that corresponding data can be fetched from the database.

因此只访问值,以便从数据库获取相应的数据。

The solution you want then should look like:

你想要的解决方案应该是:

UserContrl1_LOadDataMethod()
{
    string name = "";
    if(textbox1.InvokeRequired)
    {
        textbox1.Invoke(new MethodInvoker(delegate { name = textbox1.text; }));
    }
    if(name == "MyName")
    {
        // do whatever
    }
}

Do your serious processing in the separate thread before you attempt to switch back to the control's thread. For example:

在尝试切换回控制线程之前,在单独的线程中进行认真处理。例如:

UserContrl1_LOadDataMethod()
{
    if(textbox1.text=="MyName") //<<======Now it wont give exception**
    {
        //Load data correspondin to "MyName"
        //Populate a globale variable List<string> which will be
        //bound to grid at some later stage
        if(InvokeRequired)
        {
            // after we've done all the processing, 
            this.Invoke(new MethodInvoker(delegate {
                // load the control with the appropriate data
            }));
            return;
        }
    }
}

#2


138  

Threading Model in UI

Please read the Threading Model in UI applications in order to understand basic concepts. The link navigates to page that describes the WPF threading model. However, Windows Forms utilizes the same idea.

为了理解基本概念,请阅读UI应用程序中的线程模型。链接导航到描述WPF线程模型的页面。然而,Windows窗体采用了相同的思想。

The UI Thread

  • There is only one thread (UI thread), that is allowed to access System.Windows.Forms.Control and its subclasses members.
  • 只有一个线程(UI线程),可以访问system . window . forms。控件及其子类成员。
  • Attempt to access member of System.Windows.Forms.Control from different thread than UI thread will cause cross-thread exception.
  • 尝试访问system . window . forms的成员。与UI线程相比,来自不同线程的控制将导致跨线程异常。
  • Since there is only one thread, all UI operations are queued as work items into that thread:
  • 由于只有一个线程,所以所有UI操作都作为工作项排队到该线程中:

跨线程操作无效:从线程以外的线程访问控制。

  • If there is no work for UI thread, then there are idle gaps that can be used by a not-UI related computing.
  • 如果UI线程没有工作,那么就会有空闲的间隙,这可以被非UI相关的计算所使用。
  • In order to use mentioned gaps use System.Windows.Forms.Control.Invoke or System.Windows.Forms.Control.BeginInvoke methods:
  • 为了使用前面提到的漏洞,请使用system . window。forms。control。调用或System.Windows.Forms.Control。BeginInvoke方法:

跨线程操作无效:从线程以外的线程访问控制。

BeginInvoke and Invoke methods

  • The computing overhead of method being invoked should be small as well as computing overhead of event handler methods because the UI thread is used there - the same that is responsible for handling user input. Regardless if this is System.Windows.Forms.Control.Invoke or System.Windows.Forms.Control.BeginInvoke.
  • 被调用的方法的计算开销应该很小,因为UI线程在那里被使用,所以要计算事件处理程序方法的开销,这是负责处理用户输入的。不管这是不是system . window。forms。control。调用或System.Windows.Forms.Control.BeginInvoke。
  • To perform computing expensive operation always use separate thread. Since .NET 2.0 BackgroundWorker is dedicated to performing computing expensive operations in Windows Forms. However in new solutions you should use the async-await pattern as described here.
  • 执行计算昂贵的操作总是使用单独的线程。因为。net 2.0 BackgroundWorker致力于在Windows窗体中执行计算昂贵的操作。但是,在新的解决方案中,您应该使用这里描述的异步等待模式。
  • Use System.Windows.Forms.Control.Invoke or System.Windows.Forms.Control.BeginInvoke methods only to update a user interface. If you use them for heavy computations, your application will block:
  • 使用System.Windows.Forms.Control。调用或System.Windows.Forms.Control。BeginInvoke方法只更新用户界面。如果您使用它们进行繁重的计算,您的应用程序将会阻塞:

跨线程操作无效:从线程以外的线程访问控制。

Invoke

  • System.Windows.Forms.Control.Invoke causes separate thread to wait till invoked method is completed:
  • System.Windows.Forms.Control。调用导致单独的线程等待调用方法完成:

跨线程操作无效:从线程以外的线程访问控制。

BeginInvoke

  • System.Windows.Forms.Control.BeginInvoke doesn't cause the separate thread to wait till invoked method is completed:
  • System.Windows.Forms.Control。BeginInvoke不会导致单独的线程等待调用方法完成:

跨线程操作无效:从线程以外的线程访问控制。

Code solution

Read answers on question How to update the GUI from another thread in C#?. For C# 5.0 and .NET 4.5 the recommended solution is here.

阅读有关如何从c#中从另一个线程更新GUI的问题的答案。对于c# 5.0和。net 4.5,建议的解决方案在这里。

#3


64  

You only want to use Invoke or BeginInvoke for the bare minimum piece of work required to change the UI. Your "heavy" method should execute on another thread (e.g. via BackgroundWorker) but then using Control.Invoke/Control.BeginInvoke just to update the UI. That way your UI thread will be free to handle UI events etc.

您只需要使用Invoke或BeginInvoke来完成更改UI所需的最少工作。您的“重”方法应该在另一个线程上执行(例如,通过BackgroundWorker),然后使用Control. invoke /Control。开始调用只是为了更新UI。这样,UI线程就可以*处理UI事件等等。

See my threading article for a WinForms example - although the article was written before BackgroundWorker arrived on the scene, and I'm afraid I haven't updated it in that respect. BackgroundWorker merely simplifies the callback a bit.

请参阅我的关于WinForms示例的线程化文章——尽管这篇文章是在BackgroundWorker到达现场之前编写的,但是我恐怕在这方面还没有更新它。BackgroundWorker只是稍微简化了回调。

#4


36  

I have had this problem with the FileSystemWatcher and found that the following code solved the problem:

我在文件系统监视程序中遇到了这个问题,并发现下面的代码解决了这个问题:

fsw.SynchronizingObject = this

焊。SynchronizingObject =这

The control then uses the current form object to deal with the events, and will therefore be on the same thread.

控件然后使用当前表单对象来处理事件,因此将在同一线程上。

#5


13  

Controls in .NET are not generally thread safe. That means you shouldn't access a control from a thread other than the one where it lives. To get around this, you need to invoke the control, which is what your 2nd sample is attempting.

net中的控件通常不是线程安全的。这意味着您不应该从线程以外的线程访问控制。为了解决这个问题,您需要调用控件,这是您的第2个示例尝试的内容。

However, in your case all you've done is pass the long-running method back to the main thread. Of course, that's not really what you want to do. You need to rethink this a little so that all you're doing on the main thread is setting a quick property here and there.

但是,在您的情况中,您所做的只是将长期运行的方法返回到主线程。当然,这不是你想做的。你需要重新考虑一下,这样你在主线程上做的就是在这里和那里设置一个快速的属性。

#6


12  

I find the check-and-invoke code which needs to be littered within all methods related to forms to be way too verbose and unneeded. Here's a simple extension method which lets you do away with it completely:

我发现在所有与表单相关的方法中都需要包含检查和调用代码,这些方法过于冗长和不需要。这里有一个简单的扩展方法,可以让你完全摆脱它:

public static class Extensions
{
    public static void Invoke<TControlType>(this TControlType control, Action<TControlType> del) 
        where TControlType : Control
        {
            if (control.InvokeRequired)
                control.Invoke(new Action(() => del(control)));
            else
                del(control);
    }
}

And then you can simply do this:

然后你可以这样做:

textbox1.Invoke(t => t.Text = "A");

No more messing around - simple.

别再胡闹了——简单。

#7


10  

The cleanest (and proper) solution for UI cross-threading issues is to use SynchronizationContext, see Synchronizing calls to the UI in a multi-threaded application article, it explains it very nicely.

UI跨线程问题的最干净(和适当的)解决方案是使用同步上下文,在多线程应用程序文章中看到对UI的同步调用,它很好地解释了这一点。

#8


7  

I know its too late now. However even today if you are having trouble accessing cross thread controls? This is the shortest answer till date :P

我知道现在太晚了。但是,即使是在今天,如果您在访问交叉线程控制方面遇到困难?这是迄今为止最简短的答复。

Invoke(new Action(() =>
                {
                    label1.Text = "WooHoo!!!";
                }));

This is how i access any form control from a thread.

这是我从线程访问任何表单控件的方式。

#9


6  

A new look using Async/Await and callbacks. You only need one line of code if you keep the extension method in your project.

使用异步/等待和回调的新外观。如果您在项目中保留了扩展方法,那么您只需要一行代码。

/// <summary>
/// A new way to use Tasks for Asynchronous calls
/// </summary>
public class Example
{
    /// <summary>
    /// No more delegates, background workers etc. just one line of code as shown below
    /// Note it is dependent on the XTask class shown next.
    /// </summary>
    public async void ExampleMethod()
    {
        //Still on GUI/Original Thread here
        //Do your updates before the next line of code
        await XTask.RunAsync(() =>
        {
            //Running an asynchronous task here
            //Cannot update GUI Thread here, but can do lots of work
        });
        //Can update GUI/Original thread on this line
    }
}

/// <summary>
/// A class containing extension methods for the Task class 
/// Put this file in folder named Extensions
/// Use prefix of X for the class it Extends
/// </summary>
public static class XTask
{
    /// <summary>
    /// RunAsync is an extension method that encapsulates the Task.Run using a callback
    /// </summary>
    /// <param name="Code">The caller is called back on the new Task (on a different thread)</param>
    /// <returns></returns>
    public async static Task RunAsync(Action Code)
    {
        await Task.Run(() =>
        {
            Code();
        });
        return;
    }
}

You can add other things to the Extension method such as wrapping it in a Try/Catch statement, allowing caller to tell it what type to return after completion, an exception callback to caller:

您可以在扩展方法中添加其他的东西,例如在Try/Catch语句中包装它,允许调用者告诉它在完成后返回什么类型,一个异常回调给调用者:

Adding Try Catch, Auto Exception Logging and CallBack

添加Try Catch、Auto Exception日志和回调。

    /// <summary>
    /// Run Async
    /// </summary>
    /// <typeparam name="T">The type to return</typeparam>
    /// <param name="Code">The callback to the code</param>
    /// <param name="Error">The handled and logged exception if one occurs</param>
    /// <returns>The type expected as a competed task</returns>

    public async static Task<T> RunAsync<T>(Func<string,T> Code, Action<Exception> Error)
    {
       var done =  await Task<T>.Run(() =>
        {
            T result = default(T);
            try
            {
               result = Code("Code Here");
            }
            catch (Exception ex)
            {
                Console.WriteLine("Unhandled Exception: " + ex.Message);
                Console.WriteLine(ex.StackTrace);
                Error(ex);
            }
            return result;

        });
        return done;
    }
    public async void HowToUse()
    {
       //We now inject the type we want the async routine to return!
       var result =  await RunAsync<bool>((code) => {
           //write code here, all exceptions are logged via the wrapped try catch.
           //return what is needed
           return someBoolValue;
       }, 
       error => {

          //exceptions are already handled but are sent back here for further processing
       });
        if (result)
        {
            //we can now process the result because the code above awaited for the completion before
            //moving to this statement
        }
    }

#10


5  

You need to look at the Backgroundworker example:
http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx Especially how it interacts with the UI layer. Based on your posting, this seems to answer your issues.

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

#11


5  

I found a need for this while programming an iOS-Phone monotouch app controller in a visual studio winforms prototype project outside of xamarin stuidio. Preferring to program in VS over xamarin studio as much as possible, I wanted the controller to be completely decoupled from the phone framework. This way implementing this for other frameworks like Android and Windows Phone would be much easier for future uses.

我发现有必要在一个visual studio winforms原型项目中,在xamarin stuidio之外的visual studio winforms原型项目中编写一个iOS-Phone monotouch应用程序控制器。我希望控制器能够完全从电话框架中分离出来,更喜欢在VS上对xamarin studio进行编程。通过这种方式实现其他框架,如Android和Windows Phone,将会更容易将来使用。

I wanted a solution where the GUI could respond to events without the burden of dealing with the cross threading switching code behind every button click. Basically let the class controller handle that to keep the client code simple. You could possibly have many events on the GUI where as if you could handle it in one place in the class would be cleaner. I am not a multi theading expert, let me know if this is flawed.

我想要一个解决方案,在这个解决方案中,GUI可以对事件做出响应,而无需处理每个按钮单击后的跨线程切换代码。基本上让类控制器来处理,以保持客户机代码简单。您可能在GUI上有很多事件,如果您可以在类中的一个地方处理它,那么它将更加干净。我不是一个多面手的专家,让我知道这是否有缺陷。

public partial class Form1 : Form
{
    private ExampleController.MyController controller;

    public Form1()
    {          
        InitializeComponent();
        controller = new ExampleController.MyController((ISynchronizeInvoke) this);
        controller.Finished += controller_Finished;
    }

    void controller_Finished(string returnValue)
    {
        label1.Text = returnValue; 
    }

    private void button1_Click(object sender, EventArgs e)
    {
        controller.SubmitTask("Do It");
    }
}

The GUI form is unaware the controller is running asynchronous tasks.

GUI表单不知道控制器正在运行异步任务。

public delegate void FinishedTasksHandler(string returnValue);

public class MyController
{
    private ISynchronizeInvoke _syn; 
    public MyController(ISynchronizeInvoke syn) {  _syn = syn; } 
    public event FinishedTasksHandler Finished; 

    public void SubmitTask(string someValue)
    {
        System.Threading.ThreadPool.QueueUserWorkItem(state => submitTask(someValue));
    }

    private void submitTask(string someValue)
    {
        someValue = someValue + " " + DateTime.Now.ToString();
        System.Threading.Thread.Sleep(5000);
//Finished(someValue); This causes cross threading error if called like this.

        if (Finished != null)
        {
            if (_syn.InvokeRequired)
            {
                _syn.Invoke(Finished, new object[] { someValue });
            }
            else
            {
                Finished(someValue);
            }
        }
    }
}

#12


5  

Follow the simplest (in my opinion) way to modify objects from another thread:

遵循最简单的方法(以我的观点)从另一个线程修改对象:

using System.Threading.Tasks;
using System.Threading;

namespace TESTE
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            Action<string> DelegateTeste_ModifyText = THREAD_MOD;
            Invoke(DelegateTeste_ModifyText, "MODIFY BY THREAD");
        }

        private void THREAD_MOD(string teste)
        {
            textBox1.Text = teste;
        }
    }
}

#13


4  

This is not the recommended way to solve this error but you can suppress it quickly, it will do the job . I prefer this for prototypes or demos . add

这不是解决这个错误的建议方法,但是您可以快速地抑制它,它将完成这项工作。我更喜欢原型和演示。添加

CheckForIllegalCrossThreadCalls = false

in Form1() constructor .

在Form1()构造函数。

#14


3  

Here is an alternative way if the object you are working with doesn't have

如果您正在使用的对象没有,这里是另一种方法。

(InvokeRequired)

This is useful if you are working with the main form in a class other than the main form with an object that is in the main form, but doesn't have InvokeRequired

如果您使用的是主窗体中的主窗体,而不是主窗体中的主窗体,但没有调用该主窗体,那么这是非常有用的。

delegate void updateMainFormObject(FormObjectType objectWithoutInvoke, string text);

private void updateFormObjectType(FormObjectType objectWithoutInvoke, string text)
{
    MainForm.Invoke(new updateMainFormObject(UpdateObject), objectWithoutInvoke, text);
}

public void UpdateObject(ToolStripStatusLabel objectWithoutInvoke, string text)
{
    objectWithoutInvoke.Text = text;
}

It works the same as above, but it is a different approach if you don't have an object with invokerequired, but do have access to the MainForm

它的工作原理与上面相同,但是如果您没有一个带有invokerequired的对象,但它是一个不同的方法,但是可以访问主窗体。

#15


2  

For example to get the text from a Control of the UI thread:

例如,从UI线程的控件获取文本:

Private Delegate Function GetControlTextInvoker(ByVal ctl As Control) As String

Private Function GetControlText(ByVal ctl As Control) As String
    Dim text As String

    If ctl.InvokeRequired Then
         text = CStr(ctl.Invoke(New GetControlTextInvoker(AddressOf GetControlText), _
                           ctl))
    Else
        text = ctl.Text
    End If

Return text
End Function

#16


2  

Along the same lines as previous answers, but a very short addition that Allows to use all Control properties without having cross thread invokation exception.

和前面的答案一样,但是一个非常短的添加,允许使用所有的控件属性而不具有跨线程调用异常。

Helper Method

辅助方法

/// <summary>
/// Helper method to determin if invoke required, if so will rerun method on correct thread.
/// if not do nothing.
/// </summary>
/// <param name="c">Control that might require invoking</param>
/// <param name="a">action to preform on control thread if so.</param>
/// <returns>true if invoke required</returns>
public bool ControlInvokeRequired(Control c, Action a)
{
    if (c.InvokeRequired) c.Invoke(new MethodInvoker(delegate
    {
        a();
    }));
    else return false;

    return true;
}

Sample Usage

示例使用

// usage on textbox
public void UpdateTextBox1(String text)
{
    //Check if invoke requied if so return - as i will be recalled in correct thread
    if (ControlInvokeRequired(textBox1, () => UpdateTextBox1(text))) return;
    textBox1.Text = ellapsed;
}

//Or any control
public void UpdateControl(Color c, String s)
{
    //Check if invoke requied if so return - as i will be recalled in correct thread
    if (ControlInvokeRequired(myControl, () => UpdateControl(c, s))) return;
    myControl.Text = s;
    myControl.BackColor = c;
}

#17


2  

this.Invoke(new MethodInvoker(delegate
            {
                //your code here;
            }));

#18


0  

Same question : how-to-update-the-gui-from-another-thread-in-c

同一个问题:how-to-update-the-gui-from-another-thread-in-c

Two Ways:

两种方式:

  1. Return value in e.result and use it to set yout textbox value in backgroundWorker_RunWorkerCompleted event

    返回值在e。结果并使用它在backgroundWorker_RunWorkerCompleted事件中设置您的文本框值。

  2. Declare some variable to hold these kind of values in a separate class (which will work as data holder) . Create static instance of this class adn you can access it over any thread.

    声明某个变量在一个单独的类中保存这些值(作为数据持有者)。创建这个类adn的静态实例,您可以在任何线程*问它。

Example:

例子:

public  class data_holder_for_controls
{
    //it will hold value for your label
    public  string status = string.Empty;
}

class Demo
{
    public static  data_holder_for_controls d1 = new data_holder_for_controls();
    static void Main(string[] args)
    {
        ThreadStart ts = new ThreadStart(perform_logic);
        Thread t1 = new Thread(ts);
        t1.Start();
        t1.Join();
        //your_label.Text=d1.status; --- can access it from any thread 
    }

    public static void perform_logic()
    {
        //put some code here in this function
        for (int i = 0; i < 10; i++)
        {
            //statements here
        }
        //set result in status variable
        d1.status = "Task done";
    }
}

#19


0  

Action y; //declared inside class

y行动;/ /声明在类

label1.Invoke(y=()=>label1.Text="text");

label1.Invoke(y =()= > label1.Text =“文本”);

#20


-1  

There are two options for cross thread operations.

交叉线程操作有两种选择。

Control.InvokeRequired Property 

and second one is to use

第二个是使用。

SynchronizationContext Post Method

Control.InvokeRequired is only useful when working controls inherited from Control class while SynchronizationContext can be used anywhere. Some useful information is as following links

控制。InvokeRequired只适用于从控制类继承的工作控件,而同步上下文可以在任何地方使用。一些有用的信息是以下链接。

Cross Thread Update UI | .Net

跨线程更新UI |。net。

Cross Thread Update UI using SynchronizationContext | .Net

使用同步上下文| .Net来更新UI。

#1


352  

As per Prerak K's update comment (since deleted):

根据preak K的更新评论(自删除后):

I guess I have not presented the question properly.

我想我没有恰当地提出这个问题。

Situation is this: I want to load data into a global variable based on the value of a control. I don't want to change the value of a control from the child thread. I'm not going to do it ever from a child thread.

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

So only accessing the value so that corresponding data can be fetched from the database.

因此只访问值,以便从数据库获取相应的数据。

The solution you want then should look like:

你想要的解决方案应该是:

UserContrl1_LOadDataMethod()
{
    string name = "";
    if(textbox1.InvokeRequired)
    {
        textbox1.Invoke(new MethodInvoker(delegate { name = textbox1.text; }));
    }
    if(name == "MyName")
    {
        // do whatever
    }
}

Do your serious processing in the separate thread before you attempt to switch back to the control's thread. For example:

在尝试切换回控制线程之前,在单独的线程中进行认真处理。例如:

UserContrl1_LOadDataMethod()
{
    if(textbox1.text=="MyName") //<<======Now it wont give exception**
    {
        //Load data correspondin to "MyName"
        //Populate a globale variable List<string> which will be
        //bound to grid at some later stage
        if(InvokeRequired)
        {
            // after we've done all the processing, 
            this.Invoke(new MethodInvoker(delegate {
                // load the control with the appropriate data
            }));
            return;
        }
    }
}

#2


138  

Threading Model in UI

Please read the Threading Model in UI applications in order to understand basic concepts. The link navigates to page that describes the WPF threading model. However, Windows Forms utilizes the same idea.

为了理解基本概念,请阅读UI应用程序中的线程模型。链接导航到描述WPF线程模型的页面。然而,Windows窗体采用了相同的思想。

The UI Thread

  • There is only one thread (UI thread), that is allowed to access System.Windows.Forms.Control and its subclasses members.
  • 只有一个线程(UI线程),可以访问system . window . forms。控件及其子类成员。
  • Attempt to access member of System.Windows.Forms.Control from different thread than UI thread will cause cross-thread exception.
  • 尝试访问system . window . forms的成员。与UI线程相比,来自不同线程的控制将导致跨线程异常。
  • Since there is only one thread, all UI operations are queued as work items into that thread:
  • 由于只有一个线程,所以所有UI操作都作为工作项排队到该线程中:

跨线程操作无效:从线程以外的线程访问控制。

  • If there is no work for UI thread, then there are idle gaps that can be used by a not-UI related computing.
  • 如果UI线程没有工作,那么就会有空闲的间隙,这可以被非UI相关的计算所使用。
  • In order to use mentioned gaps use System.Windows.Forms.Control.Invoke or System.Windows.Forms.Control.BeginInvoke methods:
  • 为了使用前面提到的漏洞,请使用system . window。forms。control。调用或System.Windows.Forms.Control。BeginInvoke方法:

跨线程操作无效:从线程以外的线程访问控制。

BeginInvoke and Invoke methods

  • The computing overhead of method being invoked should be small as well as computing overhead of event handler methods because the UI thread is used there - the same that is responsible for handling user input. Regardless if this is System.Windows.Forms.Control.Invoke or System.Windows.Forms.Control.BeginInvoke.
  • 被调用的方法的计算开销应该很小,因为UI线程在那里被使用,所以要计算事件处理程序方法的开销,这是负责处理用户输入的。不管这是不是system . window。forms。control。调用或System.Windows.Forms.Control.BeginInvoke。
  • To perform computing expensive operation always use separate thread. Since .NET 2.0 BackgroundWorker is dedicated to performing computing expensive operations in Windows Forms. However in new solutions you should use the async-await pattern as described here.
  • 执行计算昂贵的操作总是使用单独的线程。因为。net 2.0 BackgroundWorker致力于在Windows窗体中执行计算昂贵的操作。但是,在新的解决方案中,您应该使用这里描述的异步等待模式。
  • Use System.Windows.Forms.Control.Invoke or System.Windows.Forms.Control.BeginInvoke methods only to update a user interface. If you use them for heavy computations, your application will block:
  • 使用System.Windows.Forms.Control。调用或System.Windows.Forms.Control。BeginInvoke方法只更新用户界面。如果您使用它们进行繁重的计算,您的应用程序将会阻塞:

跨线程操作无效:从线程以外的线程访问控制。

Invoke

  • System.Windows.Forms.Control.Invoke causes separate thread to wait till invoked method is completed:
  • System.Windows.Forms.Control。调用导致单独的线程等待调用方法完成:

跨线程操作无效:从线程以外的线程访问控制。

BeginInvoke

  • System.Windows.Forms.Control.BeginInvoke doesn't cause the separate thread to wait till invoked method is completed:
  • System.Windows.Forms.Control。BeginInvoke不会导致单独的线程等待调用方法完成:

跨线程操作无效:从线程以外的线程访问控制。

Code solution

Read answers on question How to update the GUI from another thread in C#?. For C# 5.0 and .NET 4.5 the recommended solution is here.

阅读有关如何从c#中从另一个线程更新GUI的问题的答案。对于c# 5.0和。net 4.5,建议的解决方案在这里。

#3


64  

You only want to use Invoke or BeginInvoke for the bare minimum piece of work required to change the UI. Your "heavy" method should execute on another thread (e.g. via BackgroundWorker) but then using Control.Invoke/Control.BeginInvoke just to update the UI. That way your UI thread will be free to handle UI events etc.

您只需要使用Invoke或BeginInvoke来完成更改UI所需的最少工作。您的“重”方法应该在另一个线程上执行(例如,通过BackgroundWorker),然后使用Control. invoke /Control。开始调用只是为了更新UI。这样,UI线程就可以*处理UI事件等等。

See my threading article for a WinForms example - although the article was written before BackgroundWorker arrived on the scene, and I'm afraid I haven't updated it in that respect. BackgroundWorker merely simplifies the callback a bit.

请参阅我的关于WinForms示例的线程化文章——尽管这篇文章是在BackgroundWorker到达现场之前编写的,但是我恐怕在这方面还没有更新它。BackgroundWorker只是稍微简化了回调。

#4


36  

I have had this problem with the FileSystemWatcher and found that the following code solved the problem:

我在文件系统监视程序中遇到了这个问题,并发现下面的代码解决了这个问题:

fsw.SynchronizingObject = this

焊。SynchronizingObject =这

The control then uses the current form object to deal with the events, and will therefore be on the same thread.

控件然后使用当前表单对象来处理事件,因此将在同一线程上。

#5


13  

Controls in .NET are not generally thread safe. That means you shouldn't access a control from a thread other than the one where it lives. To get around this, you need to invoke the control, which is what your 2nd sample is attempting.

net中的控件通常不是线程安全的。这意味着您不应该从线程以外的线程访问控制。为了解决这个问题,您需要调用控件,这是您的第2个示例尝试的内容。

However, in your case all you've done is pass the long-running method back to the main thread. Of course, that's not really what you want to do. You need to rethink this a little so that all you're doing on the main thread is setting a quick property here and there.

但是,在您的情况中,您所做的只是将长期运行的方法返回到主线程。当然,这不是你想做的。你需要重新考虑一下,这样你在主线程上做的就是在这里和那里设置一个快速的属性。

#6


12  

I find the check-and-invoke code which needs to be littered within all methods related to forms to be way too verbose and unneeded. Here's a simple extension method which lets you do away with it completely:

我发现在所有与表单相关的方法中都需要包含检查和调用代码,这些方法过于冗长和不需要。这里有一个简单的扩展方法,可以让你完全摆脱它:

public static class Extensions
{
    public static void Invoke<TControlType>(this TControlType control, Action<TControlType> del) 
        where TControlType : Control
        {
            if (control.InvokeRequired)
                control.Invoke(new Action(() => del(control)));
            else
                del(control);
    }
}

And then you can simply do this:

然后你可以这样做:

textbox1.Invoke(t => t.Text = "A");

No more messing around - simple.

别再胡闹了——简单。

#7


10  

The cleanest (and proper) solution for UI cross-threading issues is to use SynchronizationContext, see Synchronizing calls to the UI in a multi-threaded application article, it explains it very nicely.

UI跨线程问题的最干净(和适当的)解决方案是使用同步上下文,在多线程应用程序文章中看到对UI的同步调用,它很好地解释了这一点。

#8


7  

I know its too late now. However even today if you are having trouble accessing cross thread controls? This is the shortest answer till date :P

我知道现在太晚了。但是,即使是在今天,如果您在访问交叉线程控制方面遇到困难?这是迄今为止最简短的答复。

Invoke(new Action(() =>
                {
                    label1.Text = "WooHoo!!!";
                }));

This is how i access any form control from a thread.

这是我从线程访问任何表单控件的方式。

#9


6  

A new look using Async/Await and callbacks. You only need one line of code if you keep the extension method in your project.

使用异步/等待和回调的新外观。如果您在项目中保留了扩展方法,那么您只需要一行代码。

/// <summary>
/// A new way to use Tasks for Asynchronous calls
/// </summary>
public class Example
{
    /// <summary>
    /// No more delegates, background workers etc. just one line of code as shown below
    /// Note it is dependent on the XTask class shown next.
    /// </summary>
    public async void ExampleMethod()
    {
        //Still on GUI/Original Thread here
        //Do your updates before the next line of code
        await XTask.RunAsync(() =>
        {
            //Running an asynchronous task here
            //Cannot update GUI Thread here, but can do lots of work
        });
        //Can update GUI/Original thread on this line
    }
}

/// <summary>
/// A class containing extension methods for the Task class 
/// Put this file in folder named Extensions
/// Use prefix of X for the class it Extends
/// </summary>
public static class XTask
{
    /// <summary>
    /// RunAsync is an extension method that encapsulates the Task.Run using a callback
    /// </summary>
    /// <param name="Code">The caller is called back on the new Task (on a different thread)</param>
    /// <returns></returns>
    public async static Task RunAsync(Action Code)
    {
        await Task.Run(() =>
        {
            Code();
        });
        return;
    }
}

You can add other things to the Extension method such as wrapping it in a Try/Catch statement, allowing caller to tell it what type to return after completion, an exception callback to caller:

您可以在扩展方法中添加其他的东西,例如在Try/Catch语句中包装它,允许调用者告诉它在完成后返回什么类型,一个异常回调给调用者:

Adding Try Catch, Auto Exception Logging and CallBack

添加Try Catch、Auto Exception日志和回调。

    /// <summary>
    /// Run Async
    /// </summary>
    /// <typeparam name="T">The type to return</typeparam>
    /// <param name="Code">The callback to the code</param>
    /// <param name="Error">The handled and logged exception if one occurs</param>
    /// <returns>The type expected as a competed task</returns>

    public async static Task<T> RunAsync<T>(Func<string,T> Code, Action<Exception> Error)
    {
       var done =  await Task<T>.Run(() =>
        {
            T result = default(T);
            try
            {
               result = Code("Code Here");
            }
            catch (Exception ex)
            {
                Console.WriteLine("Unhandled Exception: " + ex.Message);
                Console.WriteLine(ex.StackTrace);
                Error(ex);
            }
            return result;

        });
        return done;
    }
    public async void HowToUse()
    {
       //We now inject the type we want the async routine to return!
       var result =  await RunAsync<bool>((code) => {
           //write code here, all exceptions are logged via the wrapped try catch.
           //return what is needed
           return someBoolValue;
       }, 
       error => {

          //exceptions are already handled but are sent back here for further processing
       });
        if (result)
        {
            //we can now process the result because the code above awaited for the completion before
            //moving to this statement
        }
    }

#10


5  

You need to look at the Backgroundworker example:
http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx Especially how it interacts with the UI layer. Based on your posting, this seems to answer your issues.

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

#11


5  

I found a need for this while programming an iOS-Phone monotouch app controller in a visual studio winforms prototype project outside of xamarin stuidio. Preferring to program in VS over xamarin studio as much as possible, I wanted the controller to be completely decoupled from the phone framework. This way implementing this for other frameworks like Android and Windows Phone would be much easier for future uses.

我发现有必要在一个visual studio winforms原型项目中,在xamarin stuidio之外的visual studio winforms原型项目中编写一个iOS-Phone monotouch应用程序控制器。我希望控制器能够完全从电话框架中分离出来,更喜欢在VS上对xamarin studio进行编程。通过这种方式实现其他框架,如Android和Windows Phone,将会更容易将来使用。

I wanted a solution where the GUI could respond to events without the burden of dealing with the cross threading switching code behind every button click. Basically let the class controller handle that to keep the client code simple. You could possibly have many events on the GUI where as if you could handle it in one place in the class would be cleaner. I am not a multi theading expert, let me know if this is flawed.

我想要一个解决方案,在这个解决方案中,GUI可以对事件做出响应,而无需处理每个按钮单击后的跨线程切换代码。基本上让类控制器来处理,以保持客户机代码简单。您可能在GUI上有很多事件,如果您可以在类中的一个地方处理它,那么它将更加干净。我不是一个多面手的专家,让我知道这是否有缺陷。

public partial class Form1 : Form
{
    private ExampleController.MyController controller;

    public Form1()
    {          
        InitializeComponent();
        controller = new ExampleController.MyController((ISynchronizeInvoke) this);
        controller.Finished += controller_Finished;
    }

    void controller_Finished(string returnValue)
    {
        label1.Text = returnValue; 
    }

    private void button1_Click(object sender, EventArgs e)
    {
        controller.SubmitTask("Do It");
    }
}

The GUI form is unaware the controller is running asynchronous tasks.

GUI表单不知道控制器正在运行异步任务。

public delegate void FinishedTasksHandler(string returnValue);

public class MyController
{
    private ISynchronizeInvoke _syn; 
    public MyController(ISynchronizeInvoke syn) {  _syn = syn; } 
    public event FinishedTasksHandler Finished; 

    public void SubmitTask(string someValue)
    {
        System.Threading.ThreadPool.QueueUserWorkItem(state => submitTask(someValue));
    }

    private void submitTask(string someValue)
    {
        someValue = someValue + " " + DateTime.Now.ToString();
        System.Threading.Thread.Sleep(5000);
//Finished(someValue); This causes cross threading error if called like this.

        if (Finished != null)
        {
            if (_syn.InvokeRequired)
            {
                _syn.Invoke(Finished, new object[] { someValue });
            }
            else
            {
                Finished(someValue);
            }
        }
    }
}

#12


5  

Follow the simplest (in my opinion) way to modify objects from another thread:

遵循最简单的方法(以我的观点)从另一个线程修改对象:

using System.Threading.Tasks;
using System.Threading;

namespace TESTE
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            Action<string> DelegateTeste_ModifyText = THREAD_MOD;
            Invoke(DelegateTeste_ModifyText, "MODIFY BY THREAD");
        }

        private void THREAD_MOD(string teste)
        {
            textBox1.Text = teste;
        }
    }
}

#13


4  

This is not the recommended way to solve this error but you can suppress it quickly, it will do the job . I prefer this for prototypes or demos . add

这不是解决这个错误的建议方法,但是您可以快速地抑制它,它将完成这项工作。我更喜欢原型和演示。添加

CheckForIllegalCrossThreadCalls = false

in Form1() constructor .

在Form1()构造函数。

#14


3  

Here is an alternative way if the object you are working with doesn't have

如果您正在使用的对象没有,这里是另一种方法。

(InvokeRequired)

This is useful if you are working with the main form in a class other than the main form with an object that is in the main form, but doesn't have InvokeRequired

如果您使用的是主窗体中的主窗体,而不是主窗体中的主窗体,但没有调用该主窗体,那么这是非常有用的。

delegate void updateMainFormObject(FormObjectType objectWithoutInvoke, string text);

private void updateFormObjectType(FormObjectType objectWithoutInvoke, string text)
{
    MainForm.Invoke(new updateMainFormObject(UpdateObject), objectWithoutInvoke, text);
}

public void UpdateObject(ToolStripStatusLabel objectWithoutInvoke, string text)
{
    objectWithoutInvoke.Text = text;
}

It works the same as above, but it is a different approach if you don't have an object with invokerequired, but do have access to the MainForm

它的工作原理与上面相同,但是如果您没有一个带有invokerequired的对象,但它是一个不同的方法,但是可以访问主窗体。

#15


2  

For example to get the text from a Control of the UI thread:

例如,从UI线程的控件获取文本:

Private Delegate Function GetControlTextInvoker(ByVal ctl As Control) As String

Private Function GetControlText(ByVal ctl As Control) As String
    Dim text As String

    If ctl.InvokeRequired Then
         text = CStr(ctl.Invoke(New GetControlTextInvoker(AddressOf GetControlText), _
                           ctl))
    Else
        text = ctl.Text
    End If

Return text
End Function

#16


2  

Along the same lines as previous answers, but a very short addition that Allows to use all Control properties without having cross thread invokation exception.

和前面的答案一样,但是一个非常短的添加,允许使用所有的控件属性而不具有跨线程调用异常。

Helper Method

辅助方法

/// <summary>
/// Helper method to determin if invoke required, if so will rerun method on correct thread.
/// if not do nothing.
/// </summary>
/// <param name="c">Control that might require invoking</param>
/// <param name="a">action to preform on control thread if so.</param>
/// <returns>true if invoke required</returns>
public bool ControlInvokeRequired(Control c, Action a)
{
    if (c.InvokeRequired) c.Invoke(new MethodInvoker(delegate
    {
        a();
    }));
    else return false;

    return true;
}

Sample Usage

示例使用

// usage on textbox
public void UpdateTextBox1(String text)
{
    //Check if invoke requied if so return - as i will be recalled in correct thread
    if (ControlInvokeRequired(textBox1, () => UpdateTextBox1(text))) return;
    textBox1.Text = ellapsed;
}

//Or any control
public void UpdateControl(Color c, String s)
{
    //Check if invoke requied if so return - as i will be recalled in correct thread
    if (ControlInvokeRequired(myControl, () => UpdateControl(c, s))) return;
    myControl.Text = s;
    myControl.BackColor = c;
}

#17


2  

this.Invoke(new MethodInvoker(delegate
            {
                //your code here;
            }));

#18


0  

Same question : how-to-update-the-gui-from-another-thread-in-c

同一个问题:how-to-update-the-gui-from-another-thread-in-c

Two Ways:

两种方式:

  1. Return value in e.result and use it to set yout textbox value in backgroundWorker_RunWorkerCompleted event

    返回值在e。结果并使用它在backgroundWorker_RunWorkerCompleted事件中设置您的文本框值。

  2. Declare some variable to hold these kind of values in a separate class (which will work as data holder) . Create static instance of this class adn you can access it over any thread.

    声明某个变量在一个单独的类中保存这些值(作为数据持有者)。创建这个类adn的静态实例,您可以在任何线程*问它。

Example:

例子:

public  class data_holder_for_controls
{
    //it will hold value for your label
    public  string status = string.Empty;
}

class Demo
{
    public static  data_holder_for_controls d1 = new data_holder_for_controls();
    static void Main(string[] args)
    {
        ThreadStart ts = new ThreadStart(perform_logic);
        Thread t1 = new Thread(ts);
        t1.Start();
        t1.Join();
        //your_label.Text=d1.status; --- can access it from any thread 
    }

    public static void perform_logic()
    {
        //put some code here in this function
        for (int i = 0; i < 10; i++)
        {
            //statements here
        }
        //set result in status variable
        d1.status = "Task done";
    }
}

#19


0  

Action y; //declared inside class

y行动;/ /声明在类

label1.Invoke(y=()=>label1.Text="text");

label1.Invoke(y =()= > label1.Text =“文本”);

#20


-1  

There are two options for cross thread operations.

交叉线程操作有两种选择。

Control.InvokeRequired Property 

and second one is to use

第二个是使用。

SynchronizationContext Post Method

Control.InvokeRequired is only useful when working controls inherited from Control class while SynchronizationContext can be used anywhere. Some useful information is as following links

控制。InvokeRequired只适用于从控制类继承的工作控件,而同步上下文可以在任何地方使用。一些有用的信息是以下链接。

Cross Thread Update UI | .Net

跨线程更新UI |。net。

Cross Thread Update UI using SynchronizationContext | .Net

使用同步上下文| .Net来更新UI。