当主线程处于活动状态时,BackgroundWorker线程中的事件未激发

本文关键字:线程 事件 BackgroundWorker 活动状态 | 更新日期: 2023-09-27 18:25:31

很长一段时间以来,我一直试图运行一个外部.bat文件(调用R脚本进行一些统计处理),并让控制台重定向到U.I.

我想我已经接近了,但就在我开始工作的时候,我遇到了一个相当大的问题!也就是说:只有当主线程结束时(通过:return;),它才有效,而不是在thread.Sleep或.WaitOne()等期间。

这是我在主线程中的代码。

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的"事件"我不会。

我的表单中的代码(上面的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);
        }
    }

我的问题是

wbOutputHandler和wbErrorHandler在控制台很好地更新时被触发,但只有当主线程退出时(使用return;)。。。。如果我在主线程中使用Thread.Sleep或.WaitOne()将控制权传递给BackgroundWorker(WatchboxWorker1_WatchExt),那么代码会成功运行,但wbOutputHandler和wbErrorHandler方法根本不会被触发。

事实上,如果我执行Thread.Sleep(10*1000),那么外部程序会按计划开始运行,10秒后,当主UI线程退出时,我会立即得到一个巨大的更新。

我不想关闭我的主线程,我想在Worker完成后继续在那里做事情!

"帮帮我堆栈溢出,你是我唯一的希望!"

当主线程处于活动状态时,BackgroundWorker线程中的事件未激发

答案是将一个backgroundWorker放在另一个为UI线程创建的backgroundWorkers中。考虑到将控制台输出打印到UI的相对简单的要求,我认为这相当复杂!

我现在从UI调用我的函数如下-

        private void btInsertBCModls_Click(object sender, EventArgs e)
        {
            BackgroundWorker bw = new BackgroundWorker();
            bw.DoWork += RC2___Scratchpad4.BC_RunExistingBCModel;
            bw.RunWorkerAsync(this);
        }

接下来我使用delegate&在我需要从另一个线程更新的任何richTextBox上调用方法-

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

呜呜!一个容易定义的问题的棘手解决方案。如果有人有更好的方法,请告诉我。感谢Carsten的帮助,真是太棒了。