技术开发 频道

Silverlight 4中四种多线程编程技术

  【IT168 专稿】上篇文章中,我们介绍了Silverlight 4编程环境下的五种多线程编程技巧。在本篇中,我们将介绍另外四种Silverlight 4多线程编程中的支持技术。

  1.使用WaitHandle

  等待句柄应当是你进行多线程编程的必备装备。由于我们的主要兴趣点在于Silverlight多线程编程相关的内容,所以我们不想再深入探讨WaitHandle。但在此为你提供一个典型的例子,告诉你使用WaitHandle的基本方法。

  清单1:

  public partial class MainPage : UserControl

  {

  AutoResetEvent handle
= new AutoResetEvent(true);

  
public MainPage()

  {

  InitializeComponent();

  
new Thread(() =>

  {

  
while (true)

  {

  handle.WaitOne();

  this.Dispatcher.BeginInvoke(()
=>

  {

  this.TextBlock1.Text
= DateTime.Now.ToString();

  });

  }

  }).Start();

  }

  
private void Button_Click(object sender, RoutedEventArgs e)

  {

  handle.Set();

  }

  }

  2.使用定时器

  System.Thread.Timer是一个多线程的计时器。这是一个简单的轻量级的计时器,使用回调方法,并由线程池中线程提供相应的服务。让我们看一个相关的例子:

  清单2:(参考示例页面TimerTestPage.xaml)

  namespace SilverlightMultiThread

  {

  
public partial class TimerTestPage : Page

  {

  System.Threading.SynchronizationContext _syncContext;

  System.Threading.Timer _timer;

  
private int _flag = 0;

  
public TimerTestPage()

  {
  {

  InitializeComponent();

  
//UI线程

  _syncContext
= System.Threading.SynchronizationContext.Current;

  
//输出当前时间

  txtMsg.Text
= DateTime.Now.ToString() + "\r\n";

  _timer
= new System.Threading.Timer(MyTimerCallback, "helltimer", 3000, 1000);

  }

  
private void MyTimerCallback(object state)

  {

  
string result = string.Format("{0} - {1}: \r\n", DateTime.Now.ToString(), (string)state);

  _syncContext.Post(delegate { txtMsg.Text
+= result; }, null);

  _flag
++;

  
if (_flag == 5)

  _timer.Change(
5000, 500);

  
else if (_flag == 10)

  _timer.Dispose();

  }

  }

  }

  有以下几点值得注意。

  第一,明确传递给定时器的参数:方法MyTimerCallback表示在线程池中执行的方法。第二个参数(在本例中的字符串)代表了传递给方法MyTimerCallback的内容。第三个参数详细说明方法MyTimerCallback被调用之前迟延时间的长短,以毫秒为单位。第四个参数是调用MyTimerCallback方法的时间间隔的说明,以毫秒为单位。

  第二,我们已经使用了SynchronizationContext对象,因为线程上下文是清晰易知的。还要注意,在方法MyTimerCallback中我们调用了它的Post方法来修改UI线程中的内容。最后,通过定时器的Change方法,我们指定在该方法执行5次后,把开始时间设置为五分钟,计时器方法调用的时间间隔为5毫秒。

  3.使用DispatcherTimer

  DispatchTimer第一次亮相是在Silverlight(WPF)中作为一个后台线程计时器。与原System.Threading.Timer相比,不同之处在于DispatchTimer是真正的在后台线程中独立执行的,而定时器Timer仍然在UI线程中执行,每隔一个指定的时间接管UI线程的控制权。总体来看,DispatchTimer主要适合于调度任务的情况。在这种情况下,我们可以根据实际要求设置等待时间。请参考下面的示例。

  清单3:

  public partial class MainPage : UserControl

  {

  DispatcherTimer
timer;

  
public MainPage()

  {

  InitializeComponent();

  
timer = new DispatcherTimer();

  
timer.Tick += (s, e) => {

  
//每隔1000毫秒发生一次

  
//修改UI线程中的对象

  this.TextBlock1.Text
= DateTime.Now.ToString();

  };

  
timer.Interval = TimeSpan.FromMilliseconds(1000);

  
timer.Start();

  }

  }

  事实上,除StoryBoard组件之外dispatcherTimer也是Silverlight编程中实现动画效果的一种重要技术。当然,我们应该当心使用dispatcherTimer有可能导致创建太多的后台线程,从而有可能导致增加CPU的负荷而降低效率。

  4.使用BackgroundWorker

  System.ComponentModel.BackgroundWorker首次出现在NET 2.0中,用于简化Windows窗体应用程序多线程交互相关的编码过程。现在,它也可用于Silverlight环境中。在后台实现中,BackgroundWorker使用了Dispatcher组件,并把所有多线程相关的复杂内容封装在一个黑盒子中,为您提供最易于使用和现成的解决方案。整体来说,BackgroundWorker非常适合从事单一的,异步的,并在后台运行的长时间的任务。

  5.使用.NET Reflector进一步跟踪观察

  现在,让我们使用.NET Reflector来进一步观察BackgroundWorker类的内容编码情形。

  清单4:

 

  public class BackgroundWorker

  {

  
// 事件声明

  
public event DoWorkEventHandler DoWork;

  
public event ProgressChangedEventHandler ProgressChanged;

  
public event RunWorkerCompletedEventHandler RunWorkerCompleted;

  
// 方法声明

  
public BackgroundWorker();

  
public void CancelAsync();

  protected virtual void OnDoWork(DoWorkEventArgs e);

  protected virtual void OnProgressChanged(ProgressChangedEventArgs e);

  protected virtual void OnRunWorkerCompleted(RunWorkerCompletedEventArgs e);

  
public void ReportProgress(int percentProgress);

  
public void ReportProgress(int percentProgress, object userState);

  
public void RunWorkerAsync();

  
public void RunWorkerAsync(object argument);

  
// 属性定义

  
public bool CancellationPending { get; }

  
public bool IsBusy { get; }

  
public bool WorkerReportsProgress { get; set; }

  
public bool WorkerSupportsCancellation { get; set; }

  }

  从各自的名称来看,你会很容易想象,上面大多成员都是常用的。为了简便起见,我们将不再进行相关的深入分析。但是,我们将构建一个具体的例子,来看一个典型的使用BackgroundWorker的案例。

  清单5:

  namespace SilverlightMultiThread

  {

  
public partial class BackgroundWorkerTestPage : Page

  {

  System.ComponentModel.BackgroundWorker _backgroundWorker;

  
public BackgroundWorkerTestPage()

  {

  InitializeComponent();

  _backgroundWorker
= new System.ComponentModel.BackgroundWorker();

  _backgroundWorker.WorkerSupportsCancellation
= true;

  _backgroundWorker.WorkerReportsProgress
= true;

  _backgroundWorker.ProgressChanged
+= new System.ComponentModel.ProgressChangedEventHandler(_backgroundWorker_ProgressChanged);

  _backgroundWorker.DoWork
+= new System.ComponentModel.DoWorkEventHandler(_backgroundWorker_DoWork);

  _backgroundWorker.RunWorkerCompleted
+= new System.ComponentModel.RunWorkerCompletedEventHandler(_backgroundWorker_RunWorkerCompleted);

  }

  
private void btnStart_Click(object sender, RoutedEventArgs e)

  {

  
if (!_backgroundWorker.IsBusy)

  _backgroundWorker.RunWorkerAsync(
"Need Parameter!");

  }

  
private void btnCancel_Click(object sender, RoutedEventArgs e)

  {

  
if (_backgroundWorker.WorkerSupportsCancellation)

  _backgroundWorker.CancelAsync();

  }

  void _backgroundWorker_DoWork(
object sender, System.ComponentModel.DoWorkEventArgs e)

  {

  
for (int i = 0; i < 10; i++)

  {

  
if ((_backgroundWorker.CancellationPending == true))

  {

  e.Cancel
= true;

  break;

  }

  
else
  {

  System.Threading.Thread.Sleep(
1000);

  _backgroundWorker.ReportProgress((i
+ 1) * 10, i);

  }

  }

  e.Result
= "Complete!";

  }

  void _backgroundWorker_ProgressChanged(
object sender, System.ComponentModel.ProgressChangedEventArgs e)

  {

  txtProgress.Text
= string.Format("Progress:{0}%; Parameter: {1}",

  e.ProgressPercentage,

  e.UserState);

  }

  void _backgroundWorker_RunWorkerCompleted(
object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)

  {

  
if (e.Error != null)

  {

  txtMsg.Text
+= e.Error.ToString() + "\r\n";

  }

  
else if (e.Cancelled)

  {

  txtMsg.Text
+= "Cancelled!\r\n";

  }

  
else

  {

  txtMsg.Text
+= e.Result.ToString() + "\r\n";

  }

  }

  }

  }

  整体而言,BackgroundWorker适合运行在一个单独的线程,特别是运行在一个非UI线程上且耗时的操作,以防止用户界面停止响应。上面,我们采用了一个“Cancel”按钮来取消线程的执行—通过判断线程是否可以取消(通过属性WorkerSupportsCancellation)。然后,我们调用方法CancelAsync暂停线程。相应地,该方法RunWorkerAsync用于启动线程,同时传递进可能的需要的参数。正如你所见,真正的异步工作是在方法_backgroundWorker_DoWork中进行的。同时,它使用ReportProgress方法报告当前进度,另外一个相关的方法_backgroundWorker_ProgressChanged用于负责在UI线程上呈现这个进度。一旦线程终止或暂停,另外一个相关方法_backgroundWorker_RunWorkerCompleted即被激活,并输出相关的提示信息。

  6.总结

  在本系列的两篇文章中,我们总结了开发Silverlight多线程应用程序的典型的技术支持。然而,编写多线程Silverlight应用程序并不容易,如文首所暗示的,我们刚刚触及了这些内容的皮毛。因此,在你的实际工作中考虑使用多线程编程技术之前,高度建议你借鉴一下微软有关的建议。老实说,选择多线程技术在很大程度上意味着你必须面对并发锁定和同步机制等技术问题。但无论如何,是否在Sivlerlight项目中引入多线程技术取决于你的最后决断。

0
相关文章