我有一个方案。(Windows形式,C#.NET)

  1. 有一个主要的形式举办一些用户控制。
  2. 用户的控制做了一些重数据的操作,这样,如果我直接打电话的 UserControl_Load 方法UI成为无响应的持续时间负载方法的执行。
  3. 为了克服这个我负载数据不同的线(在试图改变现有的码为我可以)
  4. 我用背景的工作人员的线将载的数据和完成时将通知该申,它已完成了它的工作。
  5. 现在出现了一个真正的问题。所有UI(主要形式及其子usercontrols)是建立在主要主线。在装载方法的用户控件我提取的数据基于价值观的一些控制(如文本)关的用户控件.

伪代码会是这样的:

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

但是但是但是...看来我又回到了起点。该应用程序,再次 成为停止响应。它似乎是由于所执行的线#1如果条件。装载的任务是再次通过父线并不对第三,我催生。

我不知道我是否认为这种正确或错误。我是新来的线程。

我如何解决这还有什么效果执行路线#1如果块?

情况是这样的:我要将数据加载到全球变基于价值的控制。我不想要变化价值的控制从儿童线。我不会去做它曾经从一个孩子的螺纹。

所以只有访问的价值,所以,相应的数据可获取的数据库。

有帮助吗?

解决方案

为每 Prerak K最新的评论 (由于删除):

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

情况是这样的:我要将数据加载到全球变基于价值的控制。我不想要变化价值的控制从儿童线。我不会去做它曾经从一个孩子的螺纹。

所以只有访问的价值,所以,相应的数据可获取的数据库。

解决你想要的然后应该是这样的:

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

其他提示

螺纹型的用户界面

请阅读 螺纹型的 在UI的应用程序,以便了解基本概念。这导航链接页面,介绍了WPF线程模型。然而,Windows形式采用了相同的想法。

UI线

enter image description here

enter image description here

BeginInvoke和调用的方法

enter image description here

调用

enter image description here

BeginInvoke

enter image description here

代码解决方案

读答案的问题 如何更新GUI从另外一个线程在C#?.C#5.0。净额4.5建议的解决方案是 在这里,.

您只想使用Invoke或BeginInvoke来完成更改UI所需的最少工作量。你的“沉重”方法应该在另一个线程上执行(例如通过BackgroundWorker),然后使用Control.Invoke / Control.BeginInvoke来更新UI。这样你的UI线程就可以自由地处理UI事件等。

有关线程文章。 com / ~siget / csharp / threads / winforms.shtml“rel =”noreferrer“> WinForms示例 - 虽然文章是在BackgroundWorker到达现场之前编写的,但我恐怕还没有更新它尊重。 BackgroundWorker只是稍微简化了回调。

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

fsw.SynchronizingObject = this

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

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

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

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

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

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

我发现需要在与表单相关的所有方法中散布的检查和调用代码过于冗长和不必要。这是一个简单的扩展方法,可以让你完全取消它:

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

没有更多的麻烦 - 简单。

用于UI跨线程问题的最简洁(也是正确的)解决方案是使用SynchronizationContext,请参阅在多线程应用程序中同步对UI的调用文章,它非常好地解释了它。

使用Async / Await和回调的新外观。如果在项目中保留扩展方法,则只需要一行代码。

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

您可以向Extension方法添加其他内容,例如将其包装在Try / Catch语句中,允许调用者告诉它在完成后返回什么类型,调用者的异常回调:

添加Try Catch,Auto Exception Logging和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
        }
    }

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

按照最简单的(在我看来)方式修改另一个线程中的对象:

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

我发现了一个需要为此而设计的一个iOS电话然而,文档中没有提到应用控制在一个visual studio它的原型项目之外的中stuidio.宁愿程序中VS在工作室好尽可能的,我想要控制器是完全分离的电话框架。这种方式实现这为其他框架,比如安卓窗电话会更容易为将来的使用。

我想要一个解决方案,其中GUI可能对事件作出响应而不负担的处理的交叉线开关代码后面的每一个按一下按钮。基本上我们在类控制手柄,保留客户代码的简单。你可能有很多的事件在GUI在哪里,如果你能处理它在一个地方的类将吸尘器。我不是一个多theading专家,让我知道如果这是有缺陷的。

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

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

CheckForIllegalCrossThreadCalls = false
Form1()构造函数中的

如果您使用的对象没有

,这是另一种方法
(InvokeRequired)

如果您使用主窗体以外的类中的主窗体并且主窗体中的对象处于主窗体但没有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;
}

它的工作原理与上面相同,但是如果你没有一个invokerequired的对象,但是有权访问MainForm,那么它是一种不同的方法

与以前的答案一样, 但是很短的添加允许使用所有Control属性而不会有跨线程调用异常。

帮助方法

/// <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;
}
this.Invoke(new MethodInvoker(delegate
            {
                //your code here;
            }));

例如,从UI线程的Control中获取文本:

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

同样的问题:如何更新 - 所述贵从 - 另一个线程在-C

两种方式:

  1. 在e.result中返回值并使用它在backgroundWorker_RunWorkerCompleted事件中设置yout文本框值

  2. 声明一些变量来将这些值保存在一个单独的类中(它将作为数据持有者)。创建此类的静态实例,您可以通过任何线程访问它。

  3. 示例:

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

行动y; //在类

中声明

label1.Invoke(Y =()=&GT; label1.Text = QUOT;文本&QUOT);

只需使用:

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

跨线程操作有两种选择。

Control.InvokeRequired Property 

,第二个是使用

SynchronizationContext Post Method

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

跨线程更新UI | .Net

使用SynchronizationContext 跨线程更新UI达网络

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top