我已经看过几十个与此问题相关的不同问题,每个人似乎都在推荐我已经做过的事情。我想弄清楚我做错了什么。
这是代码,它非常简单:
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();
Others
是WrapPanel
。
<WrapPanel Name="Others" Orientation="Horizontal" />
基于我见过的其他线程,UI线程应该在创建矩形时保持响应并添加到WrapPanel。但它并没有发生。用户界面挂起。
关于我做错的任何想法?
答案 0 :(得分:2)
经过一些测试后,我在不阻止用户界面的情况下完成了上述方案。
请注意,我可以使用Table 2
和async/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>
,您只需创建整个列表,然后更新公开列表的属性,这样可以避免在更新集合时出现潜在的跨线程问题,以及每个矩形的跨线程调用成本。