我对跨线程访问遇到的场景感到困惑。这是我想要做的:
主UI线程 - 菜单项单击我创建后台工作程序并以异步方式运行
private void actionSubMenuItem_Click(object sender, EventArgs e)
{
ToolStripMenuItem itemSelected = (ToolStripMenuItem)sender;
ExecuteTheActionSelected(itemSelected.Text);
}
方法ExecuteTheActionSelected
如下:
private void ExecuteTheActionSelected(string actionSelected)
{
BackgroundWorker localBackgroundWorker = new BackgroundWorker();
localBackgroundWorker.DoWork += new DoWorkEventHandler(localBackgroundWorker_DoWork);
localBackgroundWorker.RunWorkerAsync(SynchronizationContext.Current);
}
localBackgroundWorker_DoWork
有:
ActionExecutionHelper actionExecutioner = new ActionExecutionHelper()
actionExecutioner.Execute();
该类中具有方法调用程序的Execute
方法,该方法调用UI线程中的事件处理程序:
public void Execute()
{
// ---- CODE -----
new MethodInvoker(ReadStdOut).BeginInvoke(null, null);
}
protected virtual void ReadStdOut()
{
string str;
while ((str = executionProcess.StandardOutput.ReadLine()) != null)
{
object sender = new object();
DataReceivedEventArgs e = new DataReceivedEventArgs(str);
outputDataReceived.Invoke(sender, e);
//This delegate invokes UI event handler
}
}
UI事件处理程序如下:
private void executionProcess_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
if (_dwExecuteAction != null)
{
_dwExecuteAction.ShowDataInExecutionWindow(e.Text);
}
}
现在出现了跨线程问题:
public void ShowDataInExecutionWindow(string message)
{
if (rchtxtExecutionResults.InvokeRequired)
{
rchtxtExecutionResults.Invoke(new ShowDataExecutionDelegate(ShowDataInExecutionWindow), message);
}
else
{
this.rchtxtExecutionResults.AppendText(message + Environment.NewLine);
}
}
此处 Invoke不会阻止作为BeginInvoke阻止的UI 。 请帮助我理解这个场景,因为我很困惑。
答案 0 :(得分:7)
是的,这很正常。从Invoke()中获得的好处是它阻止工作线程。当您使用BeginInvoke()时,线程会保持驱动并以高于UI线程可以处理的速率发出调用请求。这取决于你要求UI线程做什么,但它开始成为每秒1000次调用的问题。
在这种情况下,UI线程停止响应,它会不断地找回另一个调用请求,同时它会消息循环并且不再执行常规任务。输入和绘画请求不再被处理。
问题的明确来源是对从进程中检索的每行输出的调用请求。它只是过快地产生它们。您需要通过降低调用率来解决此问题。对此有一个简单的规则,你只是试图保持一个人的占用,每秒调用超过25次,无论你产生什么,但眼睛模糊。因此缓冲行并测量自上次调用调用以来经过的时间量。
另请注意,使用Invoke()是一种简单的解决方法,但并不能保证能够正常工作。它是 race ,工作线程可能总是在主线程重新进入消息循环并读取下一条消息之前调用下一个Invoke()。在这种情况下,您仍然会遇到完全相同的问题。