在不冻结GUI的情况下运行另一个进程

6

我在运行外部进程时,遇到了GUI无法出现并且冻结的问题。在这种情况下,drivers.exe是一个非常简单的程序,用户只需点击“确定”即可退出。因此,每当我点击“确定”时,它就会退出。我试图让我的状态栏在drivers.exe执行时(非常快地)计数。但实际上,在drivers.exe退出之前,我的GUI根本不会出现。

private void run_drivers()
        {
            Console.WriteLine("Start Driver");
            int driver_timeout_in_minutes = 20;
            System.Diagnostics.Process driverproc = System.Diagnostics.Process.Start(Application.StartupPath + "\\" + "drivers.exe");
            driverproc.WaitForExit(driver_timeout_in_minutes * 1000 * 60);   //uses milliseconds, we must convert
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            ThreadStart worker = new ThreadStart(run_drivers);
            Console.WriteLine("Main - Creating worker thread");
            toolStripStatusLabel1.Text = "hi";
            Thread t = new Thread(worker);
            t.IsBackground = true;
            t.Start();
            Console.WriteLine("Main - Have requested the start of worker thread");

            int i = 0;
            while (t.IsAlive)
            {
                i++;
                toolStripStatusLabel1.Text = i.ToString();
            }
            Console.WriteLine("Dead");
        }
4个回答

3
你应该了解一下BackgroundWorker,因为它可以为你处理所有的线程工作。

请问你能演示一下吗?我确实试了一下,但是完全没有效果。我的表格实际上还是完全不会显示出来。 - Adam
2
问题在于“while(t.IsAlive)”循环。无论他是否使用BackgroundWorker,他都需要将其从UI线程中移除。 - Phil

2
你的表格直到drivers.exe运行后才显示的原因是因为你在Form.Load中运行了drivers.exe。 Form.Load在表单显示之前发生。尝试改为在Form.Shown中运行drivers.exe。
此外,while(t.IsAlive)将在技术上阻塞您的UI线程。如果这不是您想要的,请将其移出主线程。您还可能需要调用toolStripStatusLabel1.Refresh()来强制刷新标签,在设置其文本后。

无论代码是在Form.Load中运行还是稍后,当按照所示执行时,UI线程仍然没有时间绘制屏幕。 - Michael Petrotta

0

在GUI线程上不要放置任何循环。这会导致应用程序冻结。循环必须在后台线程上,通过Invoke方法更新toolStripStatusLabel1

看看这个例子。未经测试。或许甚至可能就像这样工作...

    private void run_drivers()
    {
        Console.WriteLine("Start Driver");
        int driver_timeout_in_minutes = 20;
        System.Diagnostics.Process driverproc = System.Diagnostics.Process.Start(Application.StartupPath + "\\" + "drivers.exe");
        int i = 0;
        var action = new Action<int>(x => {  toolStripStatusLabel1.Text = i.ToString(); })
        while (!driverproc.HasExited)
        {
            i++;
            toolStripStatusLabel1.Invoke(action);
        }

        Console.WriteLine("Dead");
        // start another thread here...
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        ThreadStart worker = new ThreadStart(run_drivers);
        Console.WriteLine("Main - Creating worker thread");
        toolStripStatusLabel1.Text = "hi";
        Thread t = new Thread(worker);
        t.IsBackground = true;
        t.Start();
        Console.WriteLine("Main - Have requested the start of worker thread");
    }

好的,那很有道理。但是我该如何等待一个函数执行完毕后再执行另一个函数呢?例如,假设我想在 func_1 和 func_2 都完成后才执行 func_2,而这两个函数都需要几分钟才能完成。 - Adam
run_drivers()方法的最后一行启动新线程。只要远离GUI线程上的循环即可。 - lubos hasko

0

一个解决方案是,在保留当前代码结构的同时,使用计时器线程定期更新UI,在线程完成时停止计时器。这样做,您将从循环转换为事件驱动设计,并且您将给UI线程时间来绘制您的表单。

// create a System.Windows.Forms.Timer through the designer or in code.
// give it a short interval if you want the counter to increment quickly.

int counter;

private void Form1_Load(object sender, EventArgs e)
{
    ...
    t.Start();
    counter = 0;
    timer.Start();
    ....
}

private void timer_Tick(object sender, EventArgs e)
{
   if (t.IsAlive)
   {
       counter++;
       toolStripStatusLabel1.Text = counter.ToString(); 
   }
   else
   {
      timer.Stop();
   }
}

(未经测试)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接