我需要执行一些高级数学计算而不冻结我的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#的线程模型。任何帮助将受到高度赞赏。 感谢。
答案 0 :(得分:1)
您不应该在并行化构造中使用Dispatcher.Invoke。至少不到这种程度。
在您的原始代码中,您的代码在工作线程中唯一真正的工作是向Dispatcher添加一些Action。你的线程实际上没有进行任何计算 - 它只是向Dispatcher引擎添加一个任务并结束,如:
ThreadPool.QueueUserWorkItem((state) =>
{
Dispatcher.Invoke(new Action(this.DoEverything));
});
和this.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:
答案 1 :(得分:1)
将其置于 Dispatcher
上将仅在UI线程上运行您的代码,最终将冻结UI。因此,只在UI线程上放置UI stuf并在辅助线程上运行耗时的操作。
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在另一个线程上运行耗时的操作,并在 RunWorkerCompleted
事件中使用UI更新代码(设置ItemsSource) BW在UI线程上运行。
答案 2 :(得分:0)
卸载CPU边界的推荐方法是通过Task Parallel Library
。您可以使用Dispatcher.Invoke
和await
的组合,或将Task.Run
与Task.Run
结合使用,而不是显式调用ContinueWith
。请注意,在使用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
不接受必须在延续内添加的计数异常处理,而await
将在Task.Run
内发生异常时抛出