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 //
}
}
我不想改变上面的逻辑是否有可能从线程函数中更新数据网格,因为我的程序被卡住了。
答案 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;
}
}