Winforms线程问题,第二个线程可以';t访问第一主窗体控件

Winforms线程问题,第二个线程可以';t访问第一主窗体控件,winforms,multithreading,Winforms,Multithreading,我有一个winforms应用程序,这个问题与线程有关。 因为我调用了“MyCustomCode(),它创建了一个新线程,并调用了该方法 “SomeMethod()”然后访问MessageBox.Show(…) 问题与线程有关,因为新创建的线程正在尝试访问 在另一个线程上创建的控件 我得到一个错误: 跨线程操作无效:从创建控件“TestForm”的线程以外的线程访问控件“TestForm” public TestForm() { InitializeComponent(); /

我有一个winforms应用程序,这个问题与线程有关。 因为我调用了“MyCustomCode(),它创建了一个新线程,并调用了该方法 “SomeMethod()”然后访问MessageBox.Show(…)

问题与线程有关,因为新创建的线程正在尝试访问 在另一个线程上创建的控件

我得到一个错误:

跨线程操作无效:从创建控件“TestForm”的线程以外的线程访问控件“TestForm”

public TestForm()
{
    InitializeComponent();


    // custom code
    //
    MyCustomCode();


}

public void SomeMethod()
{

    // ***** This causes an error  ****

    MessageBox.Show(this,   
        ex.Message, 
        "Error", 
        MessageBoxButtons.OK, 
        MessageBoxIcon.Error
    );
}



private void InitializeAutoUpdater()
{
        // Seperate thread is spun to keep polling for updates
        ThreadStart ts = new ThreadStart(SomeMethod);
        pollThread = new Thread(ts);
        pollThread.Start();
}
更新

如果你看这个例子,CheckAndUpdate方法正在调用MessageBox.Show(..),这就是我的问题所在。我本以为代码很好用


有趣的是,这段代码在周五运行得很好???

您无法从多个线程访问UI元素

解决这个问题的一种方法是调用控件的Invoke方法,并将其委托给使用UI元素(如消息框)的函数。比如:

public delegate void InvokeDelegate();

public void SomeMethod()
{

    button1.Invoke((InvokeDelegate)doUIStuff);


}


void doUIStuff()
{
           MessageBox.Show(this,   
                ex.Message, 
                "Error", 
                MessageBoxButtons.OK, 
                MessageBoxIcon.Error
            );
}

您应该而不是使用BeginInvoke,您应该使用Invoke,然后一旦您掌握了这一点,如果确实需要,您可以研究使用BeginInvoke。

  • 使用或方法

  • 使用
'*******************************************************************
'获取新处理器并在新线程上启动它。
'*******************************************************************
fpProc=新处理器(可确认、参数文件、密钥计数)
AddHandler fpProc.LogEntry,LogEntry\u处理程序的地址
读取为System.Threading.Thread=新的System.Threading.Thread(fpProc.ProcessEntry的地址)
myThread.Start()
然后在父应用程序中,您有:

'*************************************************************************
'Sub:LogEntry_Handler()
作者:罗恩·萨维奇
日期:08/29/2007
'
'此例程处理处理器类引发的日志条目事件
“在线程中运行。
'*************************************************************************
私有子LogEntry_处理程序(ByVal logLevel作为整数,ByVal logMsg作为字符串)处理fProc.LogEntry
writeLogMessage(logMsg);
端接头

这就是我要做的。

为了避免跨线程异常(InvalidOperationException),下面是代码模式:

protected delegate void someGuiFunctionDelegate(int iParam);

protected void someGuiFunction(int iParam)
{
    if (this.InvokeRequired)
    {
        someGuiFunctionDelegate dlg = new 
            someGuiFunctionDelegate(this.someGuiFunction);
        this.Invoke(dlg, new object[] { iParam });
        return;
    }

    //do something with the GUI control here
}

我同意这很烦人,但这是windows GUI控件不是线程安全的这一事实的产物。异常可以通过某个地方或其他地方的标志关闭,但不要这样做,因为这会导致很难找到bug。

多线程时的首要规则是,您绝对不能从工作线程接触UI。实现多线程有很多种方法,很难做到“正确”

这里有一篇简明扼要的文章可以帮助你


这里有一篇很长的文章,深入讨论了线程技术-

为了让事情变得简单,您可以研究如何使用这个类。此类将提供一个框架,用于处理线程和进度通知事件。您的ui线程将处理进度事件并显示您传回的错误消息。

检查invokererequired

I实际上就像递归调用一样

public delegate void InvokeDelegate(string errMessage); 

    public void SomeMethod() 
    { 
        doUIStuff("my error message");
    } 


    void doUIStuff(string errMessage) 
    { 
        if (button1.InvokeRequired)
            button1.Invoke((InvokeDelegate)doUIStuff(errMessage)); 
        else
        {
               MessageBox.Show(this,    
                    ex.Message,  
                    errMessage,  
                    MessageBoxButtons.OK,  
                    MessageBoxIcon.Error 
                ); 
        }
    } 

我知道这是一篇老文章,但我最近发现了一个使用泛型和扩展方法解决这个问题的优雅解决方案。这是作者作品和一些评论的结合

跨线程Winforms访问的通用方法


它使用事件消息队列来处理进程间通信(在本例中为线程到父级。:-)我有一个“未知数量”的线程,所有线程都将更新发送到同一父级窗口。我同意leppie。您应该检查是否需要调用,例如button1.invokererequired。如果尚未创建表单的句柄,invokererequired始终返回false。这就是为什么更推荐SynchronizationContext的原因。是因为我安装了.NET3.5吗?这是3.5版本的“功能”吗?我对此表示怀疑,但这是唯一的解释!
public static void Manipulate<T>(this T control, Action<T> action) where T : Control
{
    if (control.InvokeRequired)
    {
        control.Invoke(new Action<T, Action<T>>(Manipulate),
                    new object[] { control, action });
    }
    else
    { action(control); }
}
someLabel.Manipulate(lbl => lbl.Text = "Something");