我已经添加了一个Dispatcher,并且在按下按钮后执行命令后仍然会冻结UI。
new Thread(() =>
{
Parallel.ForEach(BootstrapNodes,
new ParallelOptions { MaxDegreeOfParallelism = 2 },
(node) =>
{
Console.WriteLine(String.Format("Currently bootstrapping {0} on {1}",
node.NodeName,
node.IPAddress));
ChefServer.BootstrapNode(node);
});
}).Start();
Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.Background, new Action(() => {
Parallel.ForEach(BootstrapNodes,
new ParallelOptions { MaxDegreeOfParallelism = 2 },
(node) =>
{
Console.WriteLine(String.Format("Currently bootstrapping {0} on {1}",
node.NodeName,
node.IPAddress));
ChefServer.BootstrapNode(node);
});
}));
我是否需要深入了解我的函数调用以避免UI冻结?我试图避免在整个地方产生线程。
编辑: 我想要指出,我的后台任务非常昂贵。
答案 0 :(得分:2)
您正在将整个lambda移动到UI线程,并在那里,您与UI异步(并行)。您应该只使用在后台计算的信息将代码放在真正更新UI的UI线程中。
// runs in a background thread
public void backgroundFoo()
{
// do heavy stuff here
var result = Work();
Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.Background, new Action(() =>
{
// update UI here after the work as been done ...
Console.WriteLine(... result.Anything ...);
}));
}
答案 1 :(得分:0)
我会使用Microsoft的Reactive Framework(Rx)。
此代码适用于您:
IDisposable subscription =
BootstrapNodes
.ToObservable()
.Select(node =>
Observable
.Start(() =>
{
Console.WriteLine(String.Format("Currently bootstrapping {0} on {1}",
node.NodeName,
node.IPAddress));
ChefServer.BootstrapNode(node);
}))
.Merge(maxConcurrent : 2)
.ObserveOnDispatcher()
.Subscribe(u => { }, () =>
{
// Back on UI thread - Code completed
});
如果您希望计算提前完成,只需致电subscription.Dispose()
。
获取WPF的位只是NuGet“System.Reactive.Windows.Threading”。
答案 2 :(得分:0)
请注意,WPF中的调度程序用于确保线程安全,而不是解冻UI 。您可以使用BackgroundWorker代替繁重的工作。
using System;
using System.ComponentModel;
using System.Threading.Tasks;
using System.Windows;
namespace ThreadingSample.WPF
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private BackgroundWorker _worker;
public MainWindow()
{
InitializeComponent();
_worker = new BackgroundWorker();
_worker.DoWork += WorkHeavy;
_worker.ProgressChanged += ReportWork;
_worker.RunWorkerCompleted += UpdateUI;
_worker.WorkerReportsProgress = true;
}
private void ReportWork(object sender, ProgressChangedEventArgs e)
{
//get node object from e.UserState
//update ui...
//Console.WriteLine(String.Format("Currently bootstrapping {0} on {1}",
//node.NodeName,
//node.IPAddress));
}
private void UpdateUI(object sender, RunWorkerCompletedEventArgs e)
{
//update ui...
}
private void WorkHeavy(object sender, DoWorkEventArgs e)
{
//heavy work....
Parallel.ForEach(BootstrapNodes,
new ParallelOptions { MaxDegreeOfParallelism = 2 },
(node) =>
{
_worker.ReportProgress(node);
ChefServer.BootstrapNode(node);
});
}
private void Button_Click(object sender, RoutedEventArgs e)
{
if (_worker.IsBusy == false)
{
_worker.RunWorkerAsync();
}
}
}
}