C# 主线程处于活动状态时BackgroundWorker线程中的事件未触发
很久以来,我一直在尝试运行一个外部的.bat文件(调用一个R脚本进行一些统计处理),并让控制台重定向到U.I 我想我很接近了,但就在我开始工作的时候,我遇到了一个相当大的问题!也就是说,它只在主线程结束后(via:return;)才工作,而不是在thread.Sleep或.WaitOne()等期间 这是我在主线程中的代码C# 主线程处于活动状态时BackgroundWorker线程中的事件未触发,c#,multithreading,process,backgroundworker,C#,Multithreading,Process,Backgroundworker,很久以来,我一直在尝试运行一个外部的.bat文件(调用一个R脚本进行一些统计处理),并让控制台重定向到U.I 我想我很接近了,但就在我开始工作的时候,我遇到了一个相当大的问题!也就是说,它只在主线程结束后(via:return;)才工作,而不是在thread.Sleep或.WaitOne()等期间 这是我在主线程中的代码 string batLoc = ALLRG___.RSCRBIN_LOC + "current.bat"; BackgroundWorker watchboxdWorker1
string batLoc = ALLRG___.RSCRBIN_LOC + "current.bat";
BackgroundWorker watchboxdWorker1 = new BackgroundWorker();
watchboxdWorker1.DoWork += frmC.WatchboxWorker1_WatchExt;
frmC.wbResetEvent = new AutoResetEvent(false);
watchboxdWorker1.RunWorkerAsync(batLoc);
//Thread.Sleep(1000*20);
//frmC.wbResetEvent.WaitOne();
return;
注意注释掉的Sleep和/或WaitOne()指令。如果我尝试使用这些,BackgroundWorker会执行,但更新U.I的“事件”不会执行
我的表格(上面的frmC)中的代码如下:
public void WatchboxWorker1_WatchExt(object sender, DoWorkEventArgs e)
{
string exeLoc = (string) e.Argument;
string arg1 = exeLoc;
string arg2 = "";
ProcessStartInfo pStartInfo = new ProcessStartInfo();
pStartInfo.FileName = exeLoc;
pStartInfo.Arguments = string.Format("\"{0}\" \"{1}\"", arg1, arg2);
pStartInfo.WorkingDirectory = Path.GetDirectoryName(exeLoc);
pStartInfo.CreateNoWindow = true;
pStartInfo.UseShellExecute = false;
pStartInfo.RedirectStandardInput = true;
pStartInfo.RedirectStandardOutput = true;
pStartInfo.RedirectStandardError = true;
Process process1 = new Process();
process1.EnableRaisingEvents = true;
process1.OutputDataReceived += new DataReceivedEventHandler(wbOutputHandler);
process1.ErrorDataReceived += new DataReceivedEventHandler(wbErrorHandler);
process1.StartInfo = pStartInfo;
process1.SynchronizingObject = rtbWatchbox;
process1.Start();
process1.BeginOutputReadLine();
process1.BeginErrorReadLine();
process1.StandardInput.Close();
process1.WaitForExit();
wbResetEvent.Set();
}
public void wbOutputHandler(Object source, DataReceivedEventArgs outLine)
{
int x = 0;
if (!String.IsNullOrEmpty(outLine.Data))
{
rtbWatchbox.AppendText(outLine.Data);
}
}
public void wbErrorHandler(Object source, DataReceivedEventArgs outLine)
{
int x = 0;
if (!String.IsNullOrEmpty(outLine.Data))
{
rtbWatchbox.AppendText(outLine.Data);
}
}
我的问题是--
WBOutputhHandler和wbErrorHandler会在控制台很好地更新时被触发——但只有在主线程退出时(使用return;)。。。。如果我在主线程中使用Thread.Sleep或.WaitOne()将控制权传递给BackgroundWorker(WatchboxWorker1_WatchText),那么代码将成功运行,但wbOutputHandler和wbErrorHandler方法根本不会被触发
事实上,如果我执行Thread.Sleep(10*1000),那么外部程序开始按计划运行,10秒钟过去了,然后当主UI线程退出时,我会立即得到一个巨大的更新
我不想关闭我的主线程,我想在工作人员完成后继续在那里做事情
[当然很高兴有更好的替代方法]
“帮我堆叠溢出,你是我唯一的希望!”答案是将backgroundWorker放在另一个backgroundWorker中,该backgroundWorker是为UI线程创建的。考虑到将控制台输出打印到UI的相对简单的要求,我认为这相当复杂 我现在从UI调用我的函数,如下所示-
private void btInsertBCModls_Click(object sender, EventArgs e)
{
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += RC2___Scratchpad4.BC_RunExistingBCModel;
bw.RunWorkerAsync(this);
}
接下来,我在需要从另一个线程更新的任何richTextBox上使用delegate&Invoke方法-
delegate void UpdateWriteboxCallback(String str);
public void wbWriteBox(string WriteString)
{
if (!String.IsNullOrEmpty(WriteString))
{
if (rtbWatchbox.InvokeRequired)
{
UpdateWriteboxCallback at = new UpdateWriteboxCallback(wbWriteBox);
this.Invoke(at, new object[] { WriteString });
}
else
{
// append richtextbox as required
}
}
}
然后在我的功能中,我使用另一个BackgroundWorker来运行控制台-
public static void BC_RunExistingBCModel(object sender, DoWorkEventArgs e)
{
RC2___RhinegoldCoreForm frmC = e.Argument as RC2___RhinegoldCoreForm;
BackgroundWorker watchboxWorker = new BackgroundWorker();
watchboxWorker.DoWork += frmC.WatchboxWorker_RunProc;
watchboxWorker.RunWorkerAsync(batLoc);
while (watchboxWorker.IsBusy)
Thread.Sleep(50);
frmC.UpdateRGCoreStatusBox4("Executed script " + m + "... ");
}
在DoWork函数中,调用上面的wbWriteBox函数
public void WatchboxWorker_RunProc(object sender, DoWorkEventArgs e)
{
string exeLoc = (string) e.Argument;
string arg1 = exeLoc;
string arg2 = "";
ProcessStartInfo pStartInfo = new ProcessStartInfo();
pStartInfo.FileName = exeLoc;
pStartInfo.Arguments = string.Format("\"{0}\" \"{1}\"", arg1, arg2);
pStartInfo.WorkingDirectory = Path.GetDirectoryName(exeLoc);
pStartInfo.CreateNoWindow = true;
pStartInfo.UseShellExecute = false;
pStartInfo.RedirectStandardInput = true;
pStartInfo.RedirectStandardOutput = true;
pStartInfo.RedirectStandardError = true;
Process process1 = new Process();
process1.EnableRaisingEvents = true;
process1.OutputDataReceived += (s, e1) => this.wbWriteBox(e1.Data);
process1.ErrorDataReceived += (s, e1) => this.wbWriteBox(e1.Data);
process1.StartInfo = pStartInfo;
process1.SynchronizingObject = rtbWatchbox;
process1.Start();
process1.BeginOutputReadLine();
process1.BeginErrorReadLine();
process1.StandardInput.Close();
process1.WaitForExit();
//wbResetEvent.Set();
}
呸!对容易定义的问题的棘手解决方案。如果有人有更好的方法,请告诉我。
感谢卡斯滕的帮助-非常棒。您不需要登录
控制台
,而是登录一些RichtTextBox
对吗?这是一个WinForms应用程序,问题是:运行UI的代码在哪里(比如application.run(new Form1());
)?顺便说一句:如果你阻止它的线程,UI当然不会更新…PS:如果你不这样退出你的应用程序,你不需要阻止!谢谢你,卡斯滕,你帮了我很大的忙。第一个代码块是一个静态方法,通过按下表单上的按钮来调用,表单设置为在启动时打开。