从while循环调用线程时,windows窗体GUI被冻结。如何让Gui响应?

时间:2011-09-21 12:22:32

标签: c# .net multithreading windows-applications

class ...
{
   onClick()
   {
      while(true)
      {
        //call the thread from here
        threadpool(request)//send request to thread using thread pool
      }
   }

   //the function for thread
   threadfunction()//function that thread calls
   {

   // I am not able to change the textbox/datagridview in windowsform from here the main ui gets stuck // 

   }
}

我不想改变上面的逻辑是否有可能从线程函数中更新数据网格,因为我的程序被卡住了。

6 个答案:

答案 0 :(得分:3)

while (true)应位于线程函数内,而不是onClick。否则GUI会被卡住,因为它无休止地执行while(true)

答案 1 :(得分:2)

根据给定的代码while(true)运行永远。如果你有一个耗时的过程,你将不得不使用一个单独的线程来处理它。如果您在主线程(UI线程)中执行该耗时过程,它将很忙,并且在完成该任务之前不会考虑您的UI更改请求。这就是你遇到UI冻结的原因。

如果您使用backgroundWorker完成耗时的任务,您将能够达到您想要的效果。您必须在BackgroundWorkder.DoWork方法的while(true)子句中实现逻辑。

有关BackgroundWorker的一些观点...

DoWork在不同的线程中,向主线程报告进度并取消异步进程是BackgroundWorker中最重要的功能。下面的例子非常清楚地展示了这三个功能。网上有大量的例子。

using System;
using System.Threading;
using System.ComponentModel;

class Program
{
  static BackgroundWorker _bw;

  static void Main()
  {
    _bw = new BackgroundWorker
    {
      WorkerReportsProgress = true,
      WorkerSupportsCancellation = true
    };
    _bw.DoWork += bw_DoWork;
    _bw.ProgressChanged += bw_ProgressChanged;
    _bw.RunWorkerCompleted += bw_RunWorkerCompleted;

    _bw.RunWorkerAsync ("Hello to worker");

    Console.WriteLine ("Press Enter in the next 5 seconds to cancel");
    Console.ReadLine();
    if (_bw.IsBusy) _bw.CancelAsync();
    Console.ReadLine();
  }

  static void bw_DoWork (object sender, DoWorkEventArgs e)
  {
    for (int i = 0; i <= 100; i += 20)
    {
      if (_bw.CancellationPending) { e.Cancel = true; return; }
      _bw.ReportProgress (i);
      Thread.Sleep (1000);      // Just for the demo... don't go sleeping
    }                           // for real in pooled threads!

    e.Result = 123;    // This gets passed to RunWorkerCompleted
  }

  static void bw_RunWorkerCompleted (object sender,
                                     RunWorkerCompletedEventArgs e)
  {
    if (e.Cancelled)
      Console.WriteLine ("You canceled!");
    else if (e.Error != null)
      Console.WriteLine ("Worker exception: " + e.Error.ToString());
    else
      Console.WriteLine ("Complete: " + e.Result);      // from DoWork
  }

  static void bw_ProgressChanged (object sender,
                                  ProgressChangedEventArgs e)
  {
    Console.WriteLine ("Reached " + e.ProgressPercentage + "%");
  }
}

请查看here了解更多详情。

答案 2 :(得分:2)

首先,不要在回调中创建一个无限循环,因为UI线程将永远运行该循环。然后,如果threadfunction()需要UI更新,则必须将代码重新同步到UI线程:

threadfunction()
{
 myControl.Update(result);
}

class TheControl
{
 public void Update(object result)
 {
  if (this.InvokeRequired)
   this.Invoke(new Action<object>(Update), result);
  else
  {
   // actual implementation
  }
 }
}

答案 3 :(得分:0)

我会假设主(gui)线程因为循环而冻结(如果你以某种方式在那里处理事件则不清楚)。另外,要从另一个线程内部更改gui,您必须调用Invoke,并使用委托更改所需的值。

答案 4 :(得分:0)

无法直接从单独的线程访问Windows窗体控件。您可能希望使用Control.Invoke更改textbox / datagridview属性
查看MSDN article.关于从单独的线程访问控件的信息。

答案 5 :(得分:0)

考虑使用async-await处理输入并在UI线程执行其他操作时更新结果。

事件处理程序:

private async void OnButton1_clicked(object sender, ...)
{
    var result = await ProcessInputAsync(...)
    displayResult(result);
}

假设ProcessInputAsync是耗时的功能。 DisplayResult由UI线程调用,可以正常处理。

  

注意:所有异步函数都应返回Task而不是void或Task <Tresult&gt;而不是TResult。有一个例外:异步事件处理程序应该返回void而不是Task。

private async Task<TResult> ProcessInputAsync(...)
{
    return await Task.Run( () => LengthyProcess(...)
}

private TResult LengthyProcess(...)
{
    // this is the time consuming process.
    // it is called by a non-ui thread
    // the ui keeps responsive
    TResult x = ...
    return x;
 }

如果你真的不想等待漫长的过程完成,但是你想要一个不同的线程更新UI你会得到一个运行时错误,一个没有创建UI元素的线程试图更新它。为此,我们有调用模式:

private void UpdateMyTextBox(string myTxt)
{
    if (this.InvokeRequired)
    {   // any other thread than the UI thread calls this function
        // invoke the UI thread to update my text box
        this.Invoke(new MethodInvoker(() => this.UpdateMyTextBox(myTxt));
    }
    else
    {
        // if here: this is the UI thread, we can access the my text box
        this.TextBox1.Text = myTxt;
    }
}