我们在UI线程中声明其一个实例,并在新线程中使用它:
private Data myData = new Data();
private void btnTest_Click(object sender, RoutedEventArgs e)
{
ThreadStart ts = new ThreadStart(this.UpdateData);
Thread newThread = new Thread(ts);
newThread.Start();
}
private void UpdateData()
{
this.myData.TheData = 5;
}
OK,不会有问题(我们暂不考虑跨多个线程访问是否安全等)。
但是,如果我们让Data类继承于DependencyObject类(其又继承于DispatcherObject类,在WPF中我们会经常这样做,因为我们要使用Dependency Property):
public class Data : DependencyObject
{
public object TheData
{
get
{
return (object)GetValue(TheDataProperty);
}
set
{
SetValue(TheDataProperty, value);
}
}
public static readonly DependencyProperty TheDataProperty =
DependencyProperty.Register("TheData", typeof(object), typeof(Data), new UIPropertyMetadata(null));
}
如果现在还按照以前的使用方式(在UI线程中声明其一个实例,并在新线程中使用它)来使用,你就会收到一个“由于其他线程拥有此对象,因此调用线程无法对其进行访问。”的System.InvalidOperationException。正确使用它的方式是使用Dispatcher的Invoke或BeginInvoke方法:
private void UpdateData()
{
if (this.myData.Dispatcher.CheckAccess())
{
this.myData.TheData = 5;
}
else
{
this.myData.Dispatcher.Invoke(DispatcherPriority.Normal, (ThreadStart)delegate
{
this.myData.TheData = 5;
});
}
}
3,对于阻塞的操作,不一定需要开启新线程
当我们遇到某个费时的操作是,第一反映往往是开启一个新线程,然后在后台去处理它,以便不阻塞我们的用户界面。当然,这是正确的想法。当并不是所有的都需如此。仔细想想界面阻塞的原因,我们知道其是由于时间被消耗在某个费时的Work Item上了,而那些处理用户界面的Work Item还在那里苦苦等候。So,我们只要别让他们苦苦等候就可以了,只要用户有界面操作我们就处理,线程上的其他空闲时间来处理我们的复杂操作。我们将复杂的费时的操作细化成很多不费时的小操作,在这些小操作之间的空隙处我们来处理相应用户界面的操作
阻塞的情况如下, MouseUp与MouseLeave会被阻塞:
(MouseDown)->(费时的,复杂操作)->(MouseUp)->(MouseLeave)…
细化后的情况如下,MouseUp与MouseLeave不会被阻塞:
(MouseDown)->(不费时的,小操作,复杂操作的1/n)->(MouseUp)->(不费时的,小操作,复杂操作的1/n) -> (MouseLeave)…
举一个简单的例子,假定我们的主界面上要显示一个数字,其为Window1的CurrentNum属性,我们已经将界面上的某个TextBlock与其绑定了起来:
<TextBlock x:Name="textBlock_ShowNum"
Text="{Binding ElementName=window1,Path=CurrentNum}"
VerticalAlignment="Center"
HorizontalAlignment="Center" />
当我们点击界面上的一个按钮后,要求该数字被不停的累加,直到再次点击该按钮是停止.实际效果相当于:
while (this.IsCalculating)
{
this.CurrentNum++;
}