即使使用WPF中的Dispatcher,按钮也会单击冻结UI

时间:2017-07-15 22:07:38

标签: c# wpf multithreading

我已经添加了一个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();

冻结ui的版本

        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冻结?我试图避免在整个地方产生线程。

编辑: 我想要指出,我的后台任务非常昂贵。

3 个答案:

答案 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();
            }
        }
    }
}