Winforms线程问题,第二个线程无法访问第一个主窗体控件

时间:2008-09-29 16:08:45

标签: winforms multithreading

我有一个winforms应用程序,问题与线程有关。 因为我正在调用'MyCustomCode()来创建一个新线程,并调用该方法 'SomeMethod()'然后访问MessageBox.Show(...)。

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

我收到错误:

跨线程操作无效:控制'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();
}

更新

如果你看一下这个例子http://www.codeproject.com/KB/cs/vanillaupdaterblock.aspx,CheckAndUpdate方法正在调用MessageBox.Show(..),这就是我的问题所在。我本以为代码很好用!

有趣的事情是这个代码在周五工作得很好???

10 个答案:

答案 0 :(得分:9)

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

解决此问题的一种方法是使用UI元素(如消息框)调用带有委托的控件的Invoke方法。有些人喜欢:

public delegate void InvokeDelegate();

public void SomeMethod()
{

    button1.Invoke((InvokeDelegate)doUIStuff);


}


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

答案 1 :(得分:7)

为了避免跨线程异常(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控件不是线程安全的事实。可以通过某处或其他地方的标志关闭异常,但不要这样做,因为它可能导致极难找到错误。

答案 2 :(得分:3)

为了简单起见,您可以查看使用BackGroundWorker类。该类将提供处理线程和进度通知事件的框架。您的ui线程将处理progress事件并显示您传回的错误消息。

答案 3 :(得分:2)

答案 4 :(得分:1)

多线程时的头号规则是绝对无法触及工作线程的UI。有很多方法可以实现多线程,并且很难让它“正确”。

这是一篇简洁的文章,可以帮助你 - Updating the UI from a Secondary Thread

这是一篇冗长的文章,深入讨论了线程 - Multi-threading in .NET

答案 5 :(得分:1)

我知道这是一篇较旧的帖子,但我最近使用泛型和扩展方法找到了一个优雅的解决方案。这是作者作品和一些评论的组合。

跨线程Winforms访问的通用方法

http://www.codeproject.com/KB/cs/GenericCrossThread.aspx

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

答案 6 :(得分:0)

你应该使用BeginInvoke,你应该使用Invoke,然后一旦你掌握了它,你可以考虑使用BeginInvoke,如果真的需要。

答案 7 :(得分:0)

'*******************************************************************
' Get a new processor and fire it off on a new thread.
'*******************************************************************
fpProc = New Processor(confTable, paramFile, keyCount)
AddHandler fpProc.LogEntry, AddressOf LogEntry_Handler
Dim myThread As System.Threading.Thread = New System.Threading.Thread(AddressOf fpProc.ProcessEntry)
myThread.Start()

然后在父应用中你有:

'*************************************************************************
'     Sub: LogEntry_Handler()
'  Author: Ron Savage
'    Date: 08/29/2007
'
' This routine handles the LogEntry events raised by the Processor class
' running in a thread.
'*************************************************************************
Private Sub LogEntry_Handler(ByVal logLevel As Integer, ByVal logMsg As String) Handles fProc.LogEntry
 writeLogMessage(logMsg);
End Sub

这就是我的工作。

答案 8 :(得分:0)

检查InvokeRequired

答案 9 :(得分:0)

我特别喜欢递归电话。

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