我有一个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(..),这就是我的问题所在。我本以为代码很好用!
有趣的事情是这个代码在周五工作得很好???
答案 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
);
}
}