【问题标题】:windows form GUI gets freezed when calling the thread from the while loop. How to make Gui responsive?从 while 循环调用线程时,Windows 窗体 GUI 会冻结。如何让 Gui 响应?
【发布时间】:2011-09-21 12:22:32
【问题描述】:
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 // 

   }
}

我不想更改上述逻辑是否可以从函数到线程同时更新数据网格,因为我的程序卡住了。

【问题讨论】:

  • 请显示真实代码,而不是您在此处提供的伪代码。阅读tinyurl.com/so-hints
  • 我认为我们需要查看 很多 更多代码,然后才能诊断此代码。 “线程的函数”到底是怎么写的?

标签: c# .net multithreading windows-applications


【解决方案1】:

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

【讨论】:

    【解决方案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了解更多详情。

    【讨论】:

      【解决方案3】:

      首先,不要在回调中创建无限循环,因为 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
        }
       }
      }
      

      【讨论】:

        【解决方案4】:

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

        【讨论】:

          【解决方案5】:

          无法从单独的线程直接访问 Windows 窗体控件。您可能想使用Control.Invoke 更改文本框/数据网格视图属性
          查看 MSDN article. 关于从单独线程访问控件的信息。

          【讨论】:

            【解决方案6】:

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

            事件处理程序:

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

            假设 ProcessInputAsync 是耗时的函数。 DisplayResult被UI线程调用,可以正常处理。

            注意:所有异步函数都应该返回 Task 而不是 void 或 Task&lt;Tresult> 而不是 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;
                }
            }
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2021-05-26
              • 1970-01-01
              • 2015-03-17
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多