我有一个mutlithreaded WPF应用程序。但是在启动一些后台线程之后,UI处于冻结状态(显示和UI输入)。 这是一个简化(但完整)的例子:
<Window x:Class="WPF_Multithreading.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<ListBox ItemsSource="{Binding Path=Scores, UpdateSourceTrigger=PropertyChanged}"/>
<ListBox Grid.Column="1" ItemsSource="{Binding Path=FrameRates, UpdateSourceTrigger=PropertyChanged}"/>
<Button Grid.Row="1"
Content="New Thread" Click="Button_NewThread_Click" />
<Button Grid.Row="1" Grid.Column="1"
Content="UI test" Click="Button_UItest_Click"/>
</Grid>
</Window>
代码隐藏:
using System.ComponentModel;
using System.Collections.ObjectModel;
using System.Threading;
namespace WPF_Multithreading
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
private List<Thread> threads;
ObservableCollection<int> _scores;
public ObservableCollection<int> Scores
{
get { return _scores; }
set { _scores = value; RaisePropertyChanged("Scores"); }
}
ObservableCollection<int> _framerates;
public ObservableCollection<int> FrameRates
{
get { return _framerates; }
set { _framerates = value; RaisePropertyChanged("FrameRates"); }
}
public MainWindow()
{
InitializeComponent();
DataContext = this;
Scores = new ObservableCollection<int>();
FrameRates = new ObservableCollection<int>();
threads = new List<Thread>();
}
~MainWindow()
{
foreach (Thread t in threads) t.Abort();
}
private void Button_NewThread_Click(object sender, RoutedEventArgs e)
{
Thread t = new Thread(backgroundRun);
Scores.Add(0);
FrameRates.Add(0);
t.Start(this);
threads.Add(t);
}
private void backgroundRun(object o)
{
MainWindow _this = (MainWindow)o;
int index = _this.Scores.Count()-1;
Random r = new Random();
DateTime prevDT = DateTime.Now;
int nbiterations = 0;
while(true)
{
// simulate computation
nbiterations = 0;
while((DateTime.Now-prevDT).Milliseconds < 50)
{
nbiterations++;
}
// Update UI
Dispatcher.Invoke(() =>
{
_this.Scores[index] = nbiterations;// r.Next();
_this.FrameRates[index] = (DateTime.Now - prevDT).Milliseconds;
});
prevDT = DateTime.Now;
}
}
private void Button_UItest_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show("UI input ok");
}
}
}
在这个例子中,我必须启动很多线程才能使UI冻结,但在我更复杂的应用程序(显示2D图形)中,只有2或3个线程可以实现(有时冻结超过10秒)。
我的问题是:有没有办法确保Dispatcher有足够的资源来解冻UI(尤其是输入)?