C# 主线程处于活动状态时BackgroundWorker线程中的事件未触发

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

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

我想我很接近了,但就在我开始工作的时候,我遇到了一个相当大的问题!也就是说,它只在主线程结束后(via: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.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:如果你不这样退出你的应用程序,你不需要阻止!谢谢你,卡斯滕,你帮了我很大的忙。第一个代码块是一个静态方法,通过按下表单上的按钮来调用,表单设置为在启动时打开。