silverlight中多线程总结

  在Silverlight中引入多线程的根本动机主要是为了解决用户体验中的响应速度,进而减少单线程带来的阻塞问题。

把我自己从测试以及其他人的文章中得出的理解分享一下,有不对的地方也请指正:

  第一条是,Silverlight只有一个主线程,也就是我们常说的UI线程。

  第二条是这个主线程实际上负责很多的事情,例如,处理用户输入,呈现页面,运行动画等等。

下面先来写一个小例子,说明一下多线程是如何减少界面假死的。

  新建一个Sliverlight项目,在MianPage上面添加一个按钮和TextBlock,按钮用来触发事件,调用方法。

  接下来写一个函数,代码如下:

 

public Int64 UpdateText()
      {
         Int64 r = 0;
         for (int i = 0; i <= 999999999; i++)
         {
            r += i;
         }
         return r;
      }

这个函数执行起来是很耗时间的,如果电脑性能差,就会更慢。

首先,写一般的调用方法:

 private void button1_Click(object sender, RoutedEventArgs e)
      {
          this.textBlock1.Text = "Result:" + UpdateText();
      }    

调试运行,点击Button按钮,发现按钮立刻变为不可用状态,过段时间,计算完成,按钮才变为可用状态。

下面来说一些多线程的方法,大家都知道.NET 中,是不允许跨线程访问的,在Silverlight中可以使用Dispatcher来刷新UI界面。

private void button1_Click(object sender, RoutedEventArgs e)
      {

         new Thread(() =>
         {
            Int64 r = 0;
            for (int i = 0; i <= 999999999; i++)
            {
               r += i;
            }

            this.Dispatcher.BeginInvoke(() =>
            {
               this.textBlock1.Text = "Current:" + r;

            });
         }).Start();

      }

这时候运行,就会发现按钮不会变为不可用的状态了。注意函数实在线程中执行的,但更新UI调用的是this.Dispatcher.BeginInvoke()方法。

不要把调用的方法写到this.Dispatcher.BeginInvoke()里面,否则界面还是会假死。

好了,下面开始说一下Sliverlight中的五中多线程机制:

1.使用Thread类

  Thread类是在Silverlight中你首先应该了解的多线程编程工具。在Thread类中定义了许多成员,相信大家在学习winform时已经了解了。

 public partial class ThreadTestPage : Page

  {

  string result = "";

  public ThreadTestPage()

  {

  InitializeComponent();

  ThreadTestMethod();

  }

  private void ThreadTestMethod()

  {

  System.Threading.Thread thread = new System.Threading.Thread(DoWork);

  thread.Name = "ThreadDemoOne";

  thread.IsBackground = true;

  thread.Start(1000);

  result += thread.IsAlive + "\r\n";

  result += thread.ManagedThreadId + "\r\n";

  result += thread.Name + "\r\n";

  result += thread.ThreadState + "\r\n";

  if (thread.Join(5000))

  {

  result += "The specified thread has terminated within 5 seconds.\r\n";

  }
txtMsg.Text = result;

  }

  void DoWork(
object sleepMillisecond)

  {

  System.Threading.Thread.Sleep((
int)sleepMillisecond);

  result 
+= "The thread terminated!\r\n";

  }

 

      第一,我们应该首先创建一个新的Thread对象。在上面的例子中,我们提供一个委托来指向要异步调用的方法。在这种情况下,DoWork是由一个后台线程(这里的委托类型省略)执行的方法。注意,ThreadStart委托不能带参数,而ParameterizedThreadStart委托可以带参数。后面的例子将展示相关的使用。

  第二,IsBackground属性指示这是否是一个后台线程(注意,在Silverlight中并没有区分是否是一个后台线程)。接下来,Start方法用于启动线程,传递一个整数来指定睡眠时间(毫秒)。请注意,Start方法立即返回,并且相关的代码开始在新线程上异步执行。事实上,我们甚至可以将任何对象传递给Start的方法。

    还要注意的是,另外一个方法Join是用来阻止调用者线程(在上面的情况下,即指主线程),直到指定的线程(在上面的情况下,即指线程thread)已完成。如果指定的线程完成,则继续执行后面的语句;如果指定的线程运行比指定的时间长,还要继续进行。返回值的意义在于指定,在指定的时间内,是否完成指定的线程执行。

2.使用System.Windows.Threading.Dispatcher

 

  我们写的第一个例子用的就是这种方法,现在来具体说一下。

 void DoWork(object sleepMillisecond)
 {
       this.Dispatcher.BeginInvoke((ThreadStart) delegate()
       {
           button1.Content = "Some text!";

       });
}

这样写的话,就可以在线程中修改控件的内容了。如果不使用Dispatcher的话,就会出现一个“无效的跨线程访问”异常。

当然还可以写成这样的形式:

 void DoWork(object sleepMillisecond)
 {
       this.Dispatcher.BeginInvoke(()=>
       {
           button1.Content = "Some text!";

       });
}

 

3.使用Deployment.Current.Dispatcher

      截至目前,上面提供的示例都是在UI控件已经启动的前提下进行的。正如我们所知道的,一般情况下Application.Current.RootVisual.Dispatcher属性引入的目的主要用于检索一个应用程序的System.Windows.Threading.Dispatcher。但是,如果在RootVisual创建之前这种操作是不会得到支持的。为了在创建RootVisual之前获得应用程序的一个调度器Dispatcher,我们可以借助于System.Windows.Deployment.Current.Dispatcher对象。

     还有另外一个情况是,在.dll程序集情况下,我们也可以通过使用Deployment.Current.Dispatcher来获得应用程序的调度器Dispatcher的一个引用。例如,要改变一个UI线程中的Silverlight控件的Text属性值,你可以使用下面的代码:

   private void Button_Click(object sender, RoutedEventArgs e)
  {

  new Thread(() =>
  {
       Deployment.Current.Dispatcher.BeginInvoke(() =>
  {

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

  });

  }).Start();

  }

注意,创建完线程,一定要Start()才会执行。

 

4.使用SynchronizationContext

      相比于以前的对象,SynchronizationContext似乎有点神秘。根据MSDN的介绍,SynchronizationContext对象能够提供各种同步模型环境下的传播同步的上下文的基本功能。据我从网上搜索的结果,结论应该是:对SynchronizationContext的发明旨在简化同步—只要你确保你是在UI线程内;否则,它会返回一个空值。因此,在大多数情况下,你可以使用Deployment.Current.Dispatcher来作为System.Windows.Deployment.Current.Dispatcher的替代。至于使用SynchronizationContext,并不是一件困难的事情。例如,你也可以如下所示来同步实现与上述类似的操作。

private void Button_Click(object sender, RoutedEventArgs e)
{
  var context = SynchronizationContext.Current;
  new Thread(() =>
  {
       context.Send((s) =>
       {
           this.TextBlock1.Text = DateTime.Now.ToString();
       }, null);
  }).Start();
}

 

5.使用线程池

      在所有的多线程解决方案中,ThreadPool应该是你最常用的技术。使用线程池的好处是明显的:

     1、它是易于控制的,而且功能也很强大,有助于降低多线程编程的整体代价;

     2、在线程池中一个线程不会由于任务的结束而灭绝,而是将继续执行其他任务,从而可以大大减少线程创建和销毁的开销。线程池提供了一个重要方法—QueueUserWorkItem。借助于此方法,能够把任何任务推入一个后台线程中,然后在后台队列中执行相应的功能。同时,它的创建也相当简单。

 private void Button_Click(object sender, RoutedEventArgs e)

  {

  ThreadPool.QueueUserWorkItem((s) =>

  {

  this.Dispatcher.BeginInvoke(() =>

  {

  int minWorkerThreads, minCompletionPortThreads, maxWorkerThreads, maxCompletionPortThreads;

  ThreadPool.GetMinThreads(out minWorkerThreads, out minCompletionPortThreads);

  ThreadPool.GetMaxThreads(out maxWorkerThreads, out maxCompletionPortThreads);

  this.TextBox1.Text = String.Format("WorkerThreads = {0} ~ {1}, CompletionPortThreads = {2} ~ {3}",

  minWorkerThreads, maxWorkerThreads, minCompletionPortThreads, maxCompletionPortThreads);

  });

  });

  }

 

先写到这里了,如果有什么不明白的,可以留言,一起交流学习。

 

 

posted @ 2014-06-11 18:15  雲霏霏  阅读(461)  评论(0编辑  收藏  举报