尝试使用Application.Current.Dispatcher.BeginInvoke

时间:2017-04-14 04:33:22

标签: c# wpf

我已经看过几十个与此问题相关的不同问题,每个人似乎都在推荐我已经做过的事情。我想弄清楚我做错了什么。

这是代码,它非常简单:

new Thread(() =>
{
    Application.Current.Dispatcher.BeginInvoke(new Action(() =>
    {
        for (int i = 0; i < 50000; i++)
        {
            Rectangle rectangle = new Rectangle()
            {
                Fill = new SolidColorBrush(Colors.Gray),
                Width = 200,
                Height = 290,
                Margin = new Thickness(5, 0, 5, 5)
            };
            Others.Children.Add(rectangle);
        }
    }));
}).Start();

OthersWrapPanel

<WrapPanel Name="Others" Orientation="Horizontal" />

基于我见过的其他线程,UI线程应该在创建矩形时保持响应并添加到WrapPanel。但它并没有发生。用户界面挂起。

关于我做错的任何想法?

2 个答案:

答案 0 :(得分:2)

经过一些测试后,我在不阻止用户界面的情况下完成了上述方案。

请注意,我可以使用Table 2async/await来实现此目的。这允许将子项添加到应用程序的Tasks中,允许在线程可用时执行工作。在此期间,仍然会发生消息,导致UI线程无法阻止。

如果您将ThreadPool包裹在WrapPanel中,您可以看到在添加新ScrollViewer时仍然可以滚动。

Rectangles

另一种方法是不要同时使用private async void OnMainWindowLoaded(object sender, RoutedEventArgs e) { for (var i = 0; i < 50000; i++) { var rect = new Rectangle { Fill = new SolidColorBrush(Colors.Gray), Width = 200, Height = 290, Margin = new Thickness(5,0,5,5) }; await Task.Run(async ()=> await AddChildAsync(rect)); } } private async Task AddChildAsync(Rectangle rect) { await Application.Current.Dispatcher.InvokeAsync(()=> Others.Children.Add(rect)); } ,而是允许Tasks切换控件来处理事件。

Dispatcher

答案 1 :(得分:1)

新线程并没有真正完成任何有用的东西。因为线程除了立即调用BeginInvoke()之外什么都没做,所以所有真正的工作都会在调度程序线程上重新启动,在那里它会阻塞该线程直到它完成。

你可以,因为one comment suggests重构循环以便循环本身在线程中,而实际的UI操作则不是。这可能看起来像这样:

new Thread(() =>
{
    for (int i = 0; i < 50000; i++)
    {
        Application.Current.Dispatcher.BeginInvoke(new Action(() =>
        {
            Rectangle rectangle = new Rectangle()
            {
                Fill = new SolidColorBrush(Colors.Gray),
                Width = 200,
                Height = 290,
                Margin = new Thickness(5, 0, 5, 5)
            };
            Others.Children.Add(rectangle);
        }));
    }
}).Start();

但我不确定那真的会做你想要的。它将在UI线程上排队50,000个单独的调用,所有这些肯定会阻碍该线程处理其他窗口消息的能力,如果不完全阻止任何其他工作,直到它们全部完成。如果不相同,净效应将与您现在看到的非常相似。

另一个问题是,这是WPF,你显然是在尝试在代码隐藏中创建UI。

如果没有某些类型的用户可感知的延迟类型,您将获得50,000个不同的Rectangle个对象,这一点并不十分清楚。但是你绝对应该考虑创建一个视图模型类型来表示实际的Rectangle对象,将它们存储在ObservableCollection<T>中,或者甚至只是一个普通的List<T>,在后台填充集合,以及让WPF处理通过Rectangle和适当的绑定创建必要的DataTemplate个对象。

如果使用该方法,使用List<T>,您只需创建整个列表,然后更新公开列表的属性,这样可以避免在更新集合时出现潜在的跨线程问题,以及每个矩形的跨线程调用成本。