[英]C# cross thread call from an event handler
我是C#和事件/代理的新手。 我有一个测试应用程序,该应用程序模仿了我在一个较大的项目中试图做的事情。 运行此程序时,我仍然收到未处理的StackOverflowException,我很清楚为什么。 我尝试将BeginInvok更改为Invoke,但这似乎导致应用程序完全挂断。 我没有正确设置吗?
这是Windows窗体代码:
public partial class Form1 : Form
{
delegate void AddProcessingEventItemCallback(string eventMessage);
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
TestClass t = new TestClass();
t.OnJobTaskStatusUpdate += new TestClass.JobTaskStatusUpdate(JobTaskStatusUpdateHandler);
Thread ts = new Thread(new ThreadStart(t.RunTest));
ts.Start();
ts.Join();
}
private void JobTaskStatusUpdateHandler(object sender, string statusMessage)
{
AddProcessingEventItem(statusMessage);
Application.DoEvents();
}
private void AddProcessingEventItem(string eventMessage)
{
if (this.listBox1.InvokeRequired)
{
AddProcessingEventItemCallback d = new AddProcessingEventItemCallback(AddProcessingEventItem);
this.BeginInvoke(d, new object[] { eventMessage });
}
else
{
listBox1.SelectedIndex = listBox1.Items.Add(eventMessage);
Application.DoEvents();
}
}
}
这是该类中的代码:
public class TestClass
{
public delegate void JobTaskStatusUpdate(object sender, string statusMessage);
public event JobTaskStatusUpdate OnJobTaskStatusUpdate;
public void RunTest()
{
for (int i = 1; i <= 500; i++)
{
UpdateJobTaskStatus(i.ToString(), true);
//System.Threading.Thread.Sleep(1000);
}
}
internal void UpdateJobTaskStatus(string statusMessage, bool addStatusTime)
{
UpdateJobTaskStatus(statusMessage, addStatusTime, false);
}
internal void UpdateJobTaskStatus(string statusMessage, bool addStatusTime, bool addLineSpacer)
{
OnJobTaskStatusUpdate(this, addStatusTime ? string.Format("{0} :\t{1}", DateTime.Now.ToString(), statusMessage) : string.Format("\t\t\t{0}", statusMessage));
if (addLineSpacer)
OnJobTaskStatusUpdate(this, "\t\t\t");
}
}
我已经搜索了一段时间了。 任何帮助将不胜感激。
您的问题从这里开始:
private void button1_Click(object sender, EventArgs e)
{
...
Thread ts = new Thread(new ThreadStart(t.RunTest));
ts.Start();
ts.Join(); // blocks the main thread
}
Join()
正在驻留您的主线程,这将导致您在整个地方使用DoEvents()
进行紧急修复。 我不确定,StackOverflow很可能来自对DoEvents()的过多重入调用。
但是更好的解决方案似乎是:使用BackgroundWorker。
您的代码包含一个不容易发现的间接递归。 发生的事情是这样的:
单击该按钮时,主线程将启动一个新线程,并等待其完成。 工作线程将沿着TestClass运行并执行n次循环。 在每次迭代中,它将通过Control.BeginInvoke排队新的AddProcessingEventItemCallback。
由于主线程在ts.Join中被阻止,但是它无法调用委托。 (这也是为什么当您执行Invoke而不是BeginInvoke时,程序将无限期阻塞的原因。工作线程正在等待主线程完成回调的执行,而主线程正在等待工作线程完成执行。)
最终,工作线程将完成其工作,并且主线程从调用返回到ts.Join。 现在,它开始执行排队的第一个回调。 当它在AddProcessingEventItem方法内部时,主线程调用Application.DoEvents()
(“ else”子句中的第二行),这使其处理下一个AddProcessingEventItemCallback委托。 因此,主线程递归(间接)调用n次AddProcessingEventItem(n是TestClass中循环迭代的次数),足以破坏堆栈。
如果仅删除else子句中对Application.DoEvents的调用,则程序将在不破坏堆栈的情况下运行。 但是,正如Henk指出的那样,更好的解决方案是根本不阻塞主线程并完全摆脱Application.DoEvents调用。 (另见乔恩的答案在这里 ,特别要注意在他的回答中最后一句。)
您的问题出在ts.Join(),它阻塞了您的GUI线程。 BackgroundWorker不是必需的,但是您应该避免Application.DoEvents()并在单独的线程中实现逻辑。 如果需要任何同步/等待,请使用互斥/信号量,ManualResetEvent等。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.