UI冻结,即使在使用线程之后
本文关键字:线程 之后 UI 冻结 | 更新日期: 2023-09-27 18:09:47
我需要在不冻结WPF UI的情况下执行一些崇高的数学计算。这是我为实现它而写的代码。
new Thread(() =>
{
ThreadPool.QueueUserWorkItem((state) =>
{
Dispatcher.Invoke(new Action(() =>
{
double x1_min = Convert.ToDouble(txt_x1_min.Text);
double x1_max = Convert.ToDouble(txt_x1_max.Text);
double x2_min = Convert.ToDouble(txt_x2_min.Text);
double x2_max = Convert.ToDouble(txt_x2_max.Text);
int iter = Convert.ToInt16(txtIterations.Text);
//Data Defining and Computing
obj.Run(x1_min, x1_max, x2_min, x2_max, iter);
myDataGrid.ItemsSource = PSOLib.table.DefaultView;
Minima.Text = string.Format("{0,0:0.000} ", PSOLib.min);
}));
});
}).Start();
我读过很多关于如何解冻它的其他主题,但我真的不太习惯C#的线程模型。如有任何帮助,我们将不胜感激。谢谢
将其放在Dispatcher
上将仅在UI线程上运行代码,最终将冻结UI。所以,只将UI stuf放在UI线程上,并在辅助线程上运行耗时的操作。
double x1_min = Convert.ToDouble(txt_x1_min.Text);
double x1_max = Convert.ToDouble(txt_x1_max.Text);
double x2_min = Convert.ToDouble(txt_x2_min.Text);
double x2_max = Convert.ToDouble(txt_x2_max.Text);
int iter = Convert.ToInt16(txtIterations.Text);
ThreadPool.QueueUserWorkItem((state) =>
{
//Data Defining and Computing
obj.Run(x1_min, x1_max, x2_min, x2_max, iter);
Dispatcher.Invoke(new Action(() =>
{
myDataGrid.ItemsSource = PSOLib.table.DefaultView;
Minima.Text = string.Format("{0,0:0.000} ", PSOLib.min);
}));
});
您还可以使用BackgroundWorker在另一个线程上运行耗时的操作,以及在UI线程本身上运行的BW的RunWorkerCompleted
事件中的UI更新代码(设置ItemsSource(。
您不应该使用Dispatcher。在并行化构造内部调用。至少没有达到这样的程度。
在原始代码中,工作线程中的代码所做的唯一实际工作就是向Dispatcher添加一些Action。您的线程实际上没有进行任何计算——它只是向Dispatcher引擎添加了一个任务并结束,比如:
ThreadPool.QueueUserWorkItem((state) =>
{
Dispatcher.Invoke(new Action(this.DoEverything));
});
还有这个。DoEverything将不会在工作线程上执行,而是在UI后台线程上执行。因此,使用工作线程的任何好处都是无效的。
您应该完成所有计算,然后再更改UI。否则,如果您在没有任何并行化的情况下直接使用它,它将不会有更多的响应。
// Complete all interactions with UI to get data before using another thread
double x1_min = Convert.ToDouble(txt_x1_min.Text);
double x1_max = Convert.ToDouble(txt_x1_max.Text);
double x2_min = Convert.ToDouble(txt_x2_min.Text);
double x2_max = Convert.ToDouble(txt_x2_max.Text);
int iter = Convert.ToInt16(txtIterations.Text);
ThreadPool.QueueUserWorkItem((state) =>
{
//Data Defining and Computing that are not dependent on any UI elements
obj.Run(x1_min, x1_max, x2_min, x2_max, iter);
var data = PSOLib.table.DefaultView;
Dispatcher.Invoke(new Action(() =>
{
//Update the UI
myDataGrid.ItemsSource = data;
Minima.Text = string.Format("{0,0:0.000} ", PSOLib.min);
}));
});
实际上,您可以尝试其他一些并行化构造,如Tasks:
卸载CPU边界工作的推荐方式是通过Task Parallel Library
。您可以使用await
和Task.Run
的组合,或者将Task.Run
与ContinueWith
一起使用,而不是显式调用Dispatcher.Invoke
。注意,当使用await
时,您需要用async
关键字标记您的方法,并让它返回Task
带await
:
double x1_min = Convert.ToDouble(txt_x1_min.Text);
double x1_max = Convert.ToDouble(txt_x1_max.Text);
double x2_min = Convert.ToDouble(txt_x2_min.Text);
double x2_max = Convert.ToDouble(txt_x2_max.Text);
int iter = Convert.ToInt16(txtIterations.Text)
var cpuConsumingTask = await Task.Run(() => obj.Run(x1_min, x1_max, x2_min, x2_max, iter));
myDataGrid.ItemsSource = PSOLib.table.DefaultView;
Minima.Text = string.Format("{0,0:0.000} ", PSOLib.min);
带ContinueWith
:
double x1_min = Convert.ToDouble(txt_x1_min.Text);
double x1_max = Convert.ToDouble(txt_x1_max.Text);
double x2_min = Convert.ToDouble(txt_x2_min.Text);
double x2_max = Convert.ToDouble(txt_x2_max.Text);
int iter = Convert.ToInt16(txtIterations.Text)
var cpuConsumingTask = Task.Run(() => obj.Run(x1_min, x1_max, x2_min, x2_max, iter)).ContinueWith(task =>
{
myDataGrid.ItemsSource = PSOLib.table.DefaultView;
Minima.Text = string.Format("{0,0:0.000} ", PSOLib.min);
}, TaskScheduler.FromCurrentSynchronizationContext());
为了可读性和易用性,我肯定会使用前者。请注意,ContinueWith
不接受计数异常处理,该处理必须添加到延续中,而如果Task.Run
内部发生异常,则await
将抛出