程序在等待线程退出时不会递增进度条

时间:2011-11-19 14:59:02

标签: c# multithreading

我有一个c#表单应用程序,用作UI并执行外部exe。我想进行一个进度条增量,直到外部exe完成执行。所以我有以下代码:

// create thread and Start external process

Thread MyNewThread = new Thread(new ThreadStart(startmodule));
MyNewThread.Start();

            do
            {
                if (progressBar1.Value < 100)
                {
                    progressBar1.Value++;
                }
            } while (MyNewThread.IsAlive); 

            label5.Text = "Status: Done";

// startmodule()
void startmodule()
    {
        ProcessObj = new Process();
        ProcessObj.StartInfo.FileName = ApplicationPath;
        ProcessObj.StartInfo.Arguments = ApplicationArguments;
        ProcessObj.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
        ProcessObj.Start();
    }

相反,它会立即填满栏并显示“完成”消息,但外部exe(AppPath)仍然在后台运行。

请发布一些我坚持的想法。我不知道什么是错的。谢谢你的时间。

6 个答案:

答案 0 :(得分:4)

循环运行得如此之快,在任务实际完成之前达到100%。正在检查循环的条件(正在运行的线程)将在您的任务完成之前成立,但循环导致进度条过早填满。

答案 1 :(得分:4)

你无法做到这一点,你无法猜测这个过程需要多长时间。将ProgressBar.Style属性设置为Marquee。启动该过程时,将Visible属性设置为true。使用Process.Exited事件将其设置为false。像这样:

public partial class Form1 : Form {
    public Form1() {
        InitializeComponent();
        progressBar1.Style = ProgressBarStyle.Marquee;
        progressBar1.Visible = false;
    }

    private void ButtonRunProcess_Click(object sender, EventArgs e) {
        var ProcessObj = new Process();
        ProcessObj.SynchronizingObject = this;
        ProcessObj.EnableRaisingEvents = true;
        ProcessObj.Exited += new EventHandler(ProcessObj_Exited);
        ProcessObj.StartInfo.FileName = @"c:\windows\notepad.exe";
        // etc...
        ProcessObj.Start();
        progressBar1.Visible = true;
    }

    void ProcessObj_Exited(object sender, EventArgs e) {
        progressBar1.Visible = false;
    }
}

答案 2 :(得分:1)

要运行进度条,您必须能够量化长时间运行任务的进度。代码中没有任何内容可以尝试量化这一点。

您需要在两个进程之间进行通信才能使此进度条正常工作。换句话说,外部进程需要将消息发送回父应用程序,通知父应用程序进度的度量。现在,这可能很难实现,因此marquee style进度条可能更合适。

答案 3 :(得分:1)

最后,我有一些“自由”时间来测试背景工作者,如上所述。我可以说这是最好的解决方案,它不会冻结用户界面。示例实现如下:

preparemodule()
{
  ProcessObj = new Process();
  ProcessObj.StartInfo.FileName = ApplicationPath;
  ProcessObj.StartInfo.Arguments = ApplicationArguments;
}

void run_Click(object sender, EventArgs e)
{
  preparemodule();
  backgroundWorker1.RunWorkerAsync(ProcessObj);
}

void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
  int i=0;

  ProcessObj.Start();

  while (checkifexists("notepad", 0) == true)
  {
    i++;
    label5.Text = "Status: notepad running... " + progressBar1.Value.ToString() + "%";
    Thread.Sleep(3000);
    backgroundWorker1.ReportProgress(i);

    if ((backgroundWorker1.CancellationPending == true))
    {
      e.Cancel = true;
    }
  }
}

void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        if (e.ProgressPercentage <= 100)
        {
            progressBar1.Value = e.ProgressPercentage;
        }
    }

 void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {

        label5.Text = "Status: Done";
    }

 void cancel_Click(object sender, EventArgs e)
    {     
        backgroundWorker1.CancelAsync();
    }

如你所见,我们甚至可以取消它。通过检查记事本是否正在运行,我们可以增加进度条。别忘了在代码中的某处启用bgWorker的“reportsprogress”和“supportscancellation”属性。我希望它有所帮助。

答案 4 :(得分:0)

首先,@ Tim的回答是正确的。

如果您可以控制外部应用程序,请与主进程通信,告知当前状态并根据这些消息更新进度条。

如果不可能,请尝试估计执行时间并根据执行时间设置进度。如果它始终在同一时间执行同一任务,则此选项有效。

答案 5 :(得分:0)

后台工作线程是为这类事情而设计的。 它有一个事件,您可以在处理某些事情时触发,您可以处理它并更新您的进度条。如其他人所指出的那样,你似乎没有任何进步的衡量标准,只是过了一段时间,所以它并不是你想要的进步的指示,而是某种“我很忙”的动画,如果你使用进步的话你可以得到各种各样的问题,这些问题会让UI男孩疯狂,就像它永远不会达到100%,或者在操作完成之前达到100%,甚至是循环。

因此,如果您可以从线程中指出一些进度,例如,如果您循环遍历X项,则每10%的X触发一次progress事件。使用后台工作线程。 如果你不能不使用进度条踢线程,可以看到一些动画控件。线程完成后,再次使动画不可见。动画的内容和方式取决于你和你的UI男孩。