开发者

windows form GUI gets freezed when calling the thread from the while loop. How to make Gui responsive?

开发者 https://www.devze.com 2023-04-06 04:53 出处:网络
class ... { onClick() { while(true) { //call the thread from here threadpool(request)//send request to thread using thread pool
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 // 

   }
}

I dont want to change the above logic is it possible to update datagrid simaltaneously from function for thread because my program just get stuck.


The while (true) should be inside the threadfunction, not in the onClick. Otherwise the GUI gets stuck because it's executing endlessly the while(true).


According to the given code while(true) runs for ever. If you have got a time consuming process you will have to use a separate thread to handle that. If you execute that time consuming process in the main thread(UI thread) it will be busy and won't take your UI change requests into account until it finish that task. That's why you experience UI freezing.

If you use a backgroundWorker for your time consuming task you will be able to achieve what you want. You have to implement the logic in the while(true) clause in the BackgroundWorkder.DoWork method.

Some points about BackgroundWorker...

DoWork in a different thread, report progress to the main thread and cancel the asynchronous process are the most important functionalities in BackgroundWorker. Below example demonstrates those three functionalities quite clearly. There are heaps of examples available on the web.

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 + "%");
  }
}

Look at here for more details.


First, don't make an infinite loop in the callback as the UI thread will then be running that loop for ever. Then, if threadfunction() requires UI updates, you must re-synchronize your code to the UI thread :

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

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


I would assume the main (gui) thread freeze because of the loop (it isn't clear if you handle events somehow in there). Also to change gui from inside another thread you have to call Invoke with the delegate changing desired values.


Windows forms controls cannot be accessed from the separate thread directly. You might want to use Control.Invoke to change textbox/datagridview properties
Check out MSDN article. about accessing controls from a separate thread.


Consider using async-await to process input and update the result while your UI thread does other things.

event handler:

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

Assuming that ProcessInputAsync is the time consuming function. DisplayResult is called by the UI thread, and can be processed normally.

Note: all async functions should return Task instead of void or Task<Tresult> instead of TResult. There is one exception: async event handlers should return void instead of 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;
 }

If you really don't want to await for the lengthy process to finish, but you want that a different thread updates the UI you get an run time error that a thread that didn't create the UI element tries to update it. For this we have the invoke pattern:

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;
    }
}
0

精彩评论

暂无评论...
验证码 换一张
取 消