跨线程操作无效:从其在其上创建的线程以外的线程访问的控件

发布于 2025-02-12 12:54:11 字数 1541 浏览 1 评论 0 原文

我有一个场景。 (Windows表单,C#,.NET)

  1. 有一个主表单,它托管某些用户控件。
  2. 用户控件执行一些繁重的数据操作,因此,如果我直接调用 usercontrol_load 方法,则UI对于执行加载方法的持续时间无反应。
  3. 为了克服此问题,我将数据加载到不同的线程上(尝试尽可能小一点地更改现有代码),
  4. 我使用了一个背景工作线程,该线程将加载数据,并在完成后通知应用程序已完成工作的应用程序。
  5. 现在是一个真正的问题。所有UI(主形式及其子usercontrols)都是在主要主线程上创建的。在UserControl的负载方法中,我是根据UserControl上某些控件的值(如TextBox)获取数据的。

伪代码看起来像这样:

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

,但似乎我还回到了Square One。再次应用程序 变得无反应。这似乎是由于条件#1的执行。加载任务再次由父螺纹完成,而不是我产生的第三个。

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

我该如何解决此问题,如果块执行1号线的执行效果是什么?

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

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

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

  1. There is a main form which hosts some user control.
  2. 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.
  3. To overcome this I load data on different thread (trying to change existing code as little as I can)
  4. 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.
  5. 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.

The pseudocode would look like this:

CODE 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

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
    }
}

But it still seems that I've come back to square one. The Application again
becomes unresponsive. 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.

I don't know whether I perceived this right or wrong.

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

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(22

好倦 2025-02-19 12:54:12

按照 prerak k's更新注释(自删除后):

我想我没有正确提出这个问题。

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

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

然后,您想要的解决方案应该看起来像:

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

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

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;
        }
    }
}

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

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;
        }
    }
}
分開簡單 2025-02-19 12:54:12

UI中的线程模型

请阅读 在UI应用程序中(旧的VB链接在这里),以了解基本概念。链接导航到描述WPF线程模型的页面。但是,Windows表单利用相同的想法。

UI线程

输入image Descriage”>

“在此处输入图像说明”

begininvoke and调用方法,

“在此处输入映像说明”

Invoke

“在此处输入图像描述”

begininvoke

“在此处输入映像说明”

代码解决方案

读取答案如何从c#?中的另一个线程更新GUI。
对于C#5.0和.NET 4.5,推荐的解决方案为在这里

Threading Model in UI

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

The UI Thread

  • There is only one thread (UI thread), that is allowed to access System.Windows.Forms.Control and its subclasses members.
  • Attempt to access member of System.Windows.Forms.Control from different thread than UI thread will cause cross-thread exception.
  • Since there is only one thread, all UI operations are queued as work items into that thread:

enter image description here

enter image description here

BeginInvoke and Invoke methods

enter image description here

Invoke

enter image description here

BeginInvoke

enter image description here

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.

难忘№最初的完美 2025-02-19 12:54:12

您只想使用 Indoke 开始使用UI所需的最小工作。您的“重”方法应在另一个线程上执行(例如 backgroundworker ),然后使用 control.invoke / control.begininvoke 仅仅以更新UI。这样,您的UI线程将可以自由处理UI事件等。

请参阅我的螺纹文章 winforms示例 - 尽管该文章是在 backgroundworker 到达现场之前写的,恐怕我还没有在这方面对其进行更新。 背景工作人员只是简化了回调。

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.

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.

浅唱々樱花落 2025-02-19 12:54:12

我知道现在为时已晚。但是,即使在今天,如果您在访问跨线程控件方面遇到困难?这是迄今为止最短的答案:P

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

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

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.

吹梦到西洲 2025-02-19 12:54:12

我对 filesystemwatcher 遇到了这个问题,发现以下代码解决了问题:

fsw.synchronizingObject = this

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

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

fsw.SynchronizingObject = this

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

风蛊 2025-02-19 12:54:12

我发现需要在与表单相关的所有方法中都乱扔垃圾的检查代码,以至于太冗长且不需要。这是一种简单的扩展方法,可以使您完全消除它:

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);
    }
}

然后您可以简单地做到这一点:

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

不再弄乱 - 简单。

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.

舞袖。长 2025-02-19 12:54:12

.NET中的控件通常不是线程安全的。这意味着您不应该从居住的线程以外的线程中访问控件。为了解决这个问题,您需要调用控件,这是您的第二个样本正在尝试的。

但是,在您的情况下,您所做的只是将长期运行的方法传递回主线程。当然,这并不是您想做的。您需要重新考虑一下,以便您在主线程上所做的所有工作都是在这里和那里设置一个快速属性。

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.

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.

披肩女神 2025-02-19 12:54:12

The cleanest (and proper) solution for UI cross-threading issues is to use SynchronizationContext, see

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.

写下不归期 2025-02-19 12:54:12

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

CheckForIllegalCrossThreadCalls = false

form1()构造函数。

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 .

明月松间行 2025-02-19 12:54:12

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

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;
        }
    }
}

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;
        }
    }
}
方觉久 2025-02-19 12:54:12

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

/// <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;
    }
}

您可以将其他内容添加到扩展方法中,例如将其包装在try/catch语句中,允许呼叫者告诉它在完成后要返回哪种类型,对呼叫者的异常回调:

添加尝试catch,自动异常记录和回调

    /// <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
        }
    }

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:

Adding Try Catch, Auto Exception Logging and CallBack

    /// <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
        }
    }
风吹雪碎 2025-02-19 12:54:12

您需要查看背景工作者示例:
http://msdn.microsoft.com/en-us/library /system.componentmodel.backgroundworker.aspx
特别是它如何与UI层相互作用。根据您的发布,这似乎可以回答您的问题。

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.

后来的我们 2025-02-19 12:54:12

简单且可重复使用的方法解决此问题。

扩展方法

public static class FormExts
{
    public static void LoadOnUI(this Form frm, Action action)
    {
        if (frm.InvokeRequired) frm.Invoke(action);
        else action.Invoke();
    }
}

样本使用

private void OnAnyEvent(object sender, EventArgs args)
{
    this.LoadOnUI(() =>
    {
        label1.Text = "";
        button1.Text = "";
    });
}

Simple and re-usable way to work around this problem.

Extension Method

public static class FormExts
{
    public static void LoadOnUI(this Form frm, Action action)
    {
        if (frm.InvokeRequired) frm.Invoke(action);
        else action.Invoke();
    }
}

Sample Usage

private void OnAnyEvent(object sender, EventArgs args)
{
    this.LoadOnUI(() =>
    {
        label1.Text = "";
        button1.Text = "";
    });
}
挽梦忆笙歌 2025-02-19 12:54:12

如果您使用的对象没有使用的对象,则

(InvokeRequired)

这 是一种替代方法IndokeRequrequiend

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;
}

效果与上述相同,但是如果您没有InvokeRequred的对象,则是另一种方法,但确实可以访问Mainform

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

绝不放开 2025-02-19 12:54:12

在Xamarin Stuidio之外,在Visual Studio Winforms Prototype Project中编程iOS电话单触摸应用程序控制器时,我发现了这一点。我希望尽可能多地在Xamarin Studio上进行编程,我希望控制器完全与电话框架完全解耦。这样,对于其他框架,例如Android和Windows Phone,对于将来的用途而言,将更加容易。

我想要一个解决方案,其中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");
    }
}

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);
            }
        }
    }
}

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.

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.

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.

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);
            }
        }
    }
}
缪败 2025-02-19 12:54:12

与以前的答案相同的行
但是,一个很短的添加允许使用所有控制属性而无需跨线引用异常。

助手方法

/// <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;
}

样品使用

// 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;
}

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;
}
诠释孤独 2025-02-19 12:54:12

例如,从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

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

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
短暂陪伴 2025-02-19 12:54:12
this.Invoke(new MethodInvoker(delegate
            {
                //your code here;
            }));
this.Invoke(new MethodInvoker(delegate
            {
                //your code here;
            }));
自演自醉 2025-02-19 12:54:12

相同的问题-from-Another-thread-in-c

两种方法:

  1. 返回e.result中的值,并使用它来设置yout textbox value事件

  2. 。创建此类adn的静态实例您可以通过任何线程访问它。

例子:

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";
    }
}

Same question : 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

  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.

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";
    }
}
违心° 2025-02-19 12:54:12

只需使用以下方式:

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

Simply use this:

this.Invoke((MethodInvoker)delegate
            {
                YourControl.Property= value; // runs thread safe
            });
温柔嚣张 2025-02-19 12:54:12

行动y; //在class

label1.invoke中声明(y =()=&gt; label1.text =“ text”);

Action y; //declared inside class

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

余罪 2025-02-19 12:54:12

跨线程操作有两个选择。

Control.InvokeRequired Property 

第二个是使用

SynchronizationContext Post Method

Control.InVokereQured仅当从控制类继承的工作控件而同时可以在任何地方使用SynchronizationContext。一些有用的信息如下:链接

交叉线程更新UI | 。

​使用SynchronizationContext | .net

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

Cross Thread Update UI | .Net

Cross Thread Update UI using SynchronizationContext | .Net

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文