写点什么

Silverlight 的多线程能力(上)

  • 2011-03-08
  • 本文字数:2940 字

    阅读完需:约 10 分钟

对于多线程其实一直以来都存在很多误区:比如多任务与多线程就很容易被混为一谈,而多线程也常被理所应当的认为是并行等等。而事实却是:多任务≠多线程、单任务≠单线程、多线程不一定并行,多线程与性能不成线性关系等等,其中道理在这里不再详述。笔者认为Silverlight 多线程主要作用不是在于提高性能,而是在于用户体验,其根本目的是解决用户体验中的响应速度,减少单线程带来的阻塞问题。用一个贴切的例子来形容单线程和多线程的区别:单线程就好像只有一个服务窗口卖票的车站,人们排队买票时都是单线程处理的,而且不能抢夺位置,这样只要前方有一个人出现长时间等待,后面的人都不能被响应,这就出现了单线程阻塞;而多线程就好像有多个服务窗口去卖票,这样车票买卖和等待的情况就会好很多(当然这个例子如果换成公共厕所,对于用户体验就显得更为重要了)。

这次我们就要来看看Silverlight 的多线程能力,其实Silverlight 的多线程体现在两大方面:

第一方面是将UI 线程与后台工作线程的分离,使得UI 线程可以更好地响应用户操作,而后台线程处理完后,允许通过异步的方式将处理结果推回前台进行展示。笔者认为这是多线程在Silverlight 中最主要的作用(很多传统Web 应用开发者在刚开始接触Silverlight 时很不适应这种前后台线程的异步操作)。

第二方面是对后台作业的多线程支持,比如当需要在客户端后台并行运算时,你可以通过发起多个线程来完成这些运算。在上期《Silverlight CoreCLR 结构浅析》中,我已经给大家介绍了Silverlight 的基础类库,其中就包括多线程的相关类集。

UI 线程是 Silverlight 与用户交互的线程,在 Silverlight 中 UI 线程是单一的,其中装入的是 UI 控件类及用于数据绑定的 View Model 类(什么是 View Model?就是只为 View 层服务的实体,如果要展开说会很长,就此打住!),而在后台线程中是不能直接访问这些 UI 线程中的数据与控件对象的属性。但大家不用担心,Silverlight 和 WPF 的线程模型都使用了类似于 Java Swing 中 EDT(Event Dispatch Thread)这种安全的事件分发线程模型来解决 UI 线程与其他后台线程的数据互访问题。在 Silverlight(WPF)的控件类库 System.Windows 下所有类都继承了 DependencyObject 基类,DependencyObject 类不仅提供了 Silverlight(WPF)最基础的依赖性属性服务(什么是依赖性属性?简单的说就是对象属性值依赖于其他计算值的方式,这种方式为数据绑定、动画、重用样式都提供了可行性,这里不再展开),同时也开启了 UI 线程与后台线程的数据互访通道,在 DependencyObject 中有一个非常重要的属性——Dispatcher,后台线程可以通过调用发起者(一般都是 UI 控件)的 Dispatcher 来实现互操作,后台线程可以通过下面的方式来直接操作 UI 线程中的对象:

复制代码
_UISender.Dispatcher.BeginInvoke(() =>
{
// 这里可以访问 UI 线程中的对象,因为这个委托本身就在 UI 线程中执行
}

上面的 ()=> 是 Lamda 表达式中对于无入参的委托方法的简写形式,如果有传入参数可以在括号中列明,当然你也可以使用 Action 各种重载到其他地方实现委托过程。

如果要实现 UI 线程创建并访问后台线程就更加简单,Silverlight 提供了多种创建后台线程的方式:

  • 基于普通的 System.Threading.Thread 类创建后台线程
    Thread 类是最基础的多线程类,它可以创建一个独立运行的线程,比如:
复制代码
Threadthread = new Thread(obj.functionName);
thread.IsBackground = true;
thread.Start();

但 Thread 对于线程的监控、销毁、回调都比较复杂,因此笔者往往使用 Thread 来完成一些简单的且不需要回调的任务。

  1. 基于 System.Windows.Threading.DispatchTimer 类创建后台定时器线程
    DispatchTimer 类是 Silverlight(WPF)里才出现的后台线程定时器,相较于原有 System.Threading.Timer 差别在于 DispatchTimer 是真正的在后台线程内独立执行,而 Timer 仍然在 UI 线程中执行,只是定时获得 UI 线程控制权而已。DispatchTimer 只适合于定时执行的任务,你可以根据需要来设置等待时歇,其创建方式如下:
复制代码
DispatcherTimer dt = new DispatcherTimer();
dt.Interval = new TimeSpan(0, 0, 0, 0, 10);
dt.Tick += new EventHandler(dt_Tick);
dt.Start();
void dt_Tick(object sender, EventArgs e)
{
// 超过等待时歇时发生
// 这里可以访问 UI 线程中的对象
// 如果要结束定时器可以调用 dt.Stop();
}

DispatcherTimer 其实也是除 StoryBoard 外可以实现动画的重要组件,当然要慎用 DispatcherTimer 来构建过多后台线程,否则会使 CPU 调度开销增加反而影响效率!(调度开销将在下部分讲解)

  1. 基于 System.ComponentModel.BackgroundWorker 类轻松创建后台线程
    微软在 WinForm 架构中就引入了 BackgroundWorker 类,这个类内建了许多线程包装方法,从而大大简化线程交互的编码过程。在 Silverlight(WPF)中也可以通过 BackgroundWorker 类来轻松创建后台线程,其创建方式如下:
复制代码
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += new DoWorkEventHandler((object, doworkeventarg) =>obj.function());
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
if(!bw.IsBusy) bw.RunWorkerAsync();
void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// 任务完成时回调事件
}

BackgroundWorker 也可以通过 ReportProgress(intpercentProgress) 方法来向其他线程报告其进度完成情况,当然这只适合于可量化进度的后台工作线程,其实现如下:

复制代码
// 后台线程 obj.function() 中
obj.ReportProgress(i);
// 在 UI 线程中定义报告时间委托
bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// 显示进度的相关处理方法
}
  1. 基于 System.Threading.ThreadPool 静态类创建后台线程
    在所有多线程解决方案中,ThreadPool 线程池是笔者最常用的技术。其优点在于易于控制并且减少开销,在线程池中的线程不会由于完成一个任务就消亡,而是会继续执行其他的任务,大大减少了线程的创建与销毁开销。ThreadPool 中的 QueueUserWorkItem 方法可以将任何处理函数排入后台线程队列中执行,其创建方式也非常简单:
复制代码
obj.OnEvent += (object, eventarg) => Dispatcher.BeginInvoke(UI_OnEvent);
ThreadPool.QueueUserWorkItem(state =>obj.function(), stat);
voidUI_OnEvent()
{
// 后台线程事件发生时的回调事件
}

在后台线程对象 obj 中你可以随意定义回调事件,并通过 Dispatcher.BeginInvoke 的方法来通知 UI 线程的委托。这样的方式比较简单而且实用。当然 ThreadPool 类还提供了 RegisterWaitForSingleObject 方法来实现 Timer 定时器的功能,可以说 ThreadPool 是在企业应用中比较常用的多线程实现类。

至此,就给大家介绍了 Silverlight 常用多线程实现方式,但笔者还要强调的是:Silverlight 的多线程是为了提升用户体验。其实 Silverlight 开发围绕的关键是用户体验,用户体验在现代商业应用开发中的地位非常的重要。本主题的下半部分,笔者将通过一个实例来为大家讲述 Silverlight 的多线程性能,以及与其他 Web 开发技术的性能对比。

2011-03-08 23:123720

评论

发布
暂无评论
发现更多内容

朋友不讲武德急催我给他Java干货教程,我劝他耗子尾汁并丢给他一份GitHub上标星115k+的Java教程,他看了之后连忙向我道歉!

Java架构之路

Java 程序员 架构 面试 编程语言

SpringCloud Alibaba微服务实战八 - Seata 整合Nacos

AI乔治

Java 架构 微服务 Spring Cloud

对话机器人70年:科幻与现实的交融

华为云开发者联盟

AI 机器人 对话

年轻人不讲武德!Security五套「源码级」笔记哪里来的?

小Q

学习 编程 面试 spring security SpringCloud

SpringCloud Alibaba微服务实战七 - 分布式事务

AI乔治

Java 架构 微服务 Spring Cloud

如何在ForeSpider数据采集器中设置代理IP

前嗅大数据

大数据 爬虫 数据采集 代理IP 代理IP设置

区块链+数字版权:区块链助力版权保护

13530558032

第11代酷睿处理器出色体验的奥秘原来是这个!

E科讯

SpringCloud Alibaba微服务实战三 - 服务调用

AI乔治

Java 架构 微服务 Spring Cloud

SpringCloud Alibaba微服务实战四 - 版本管理

AI乔治

Java 架构 微服务 Spring Cloud

从前世今生聊一聊,大厂为啥亲睐时序数据库

华为云开发者联盟

数据库 场景 时序

阿里P8熬夜完成这两份800页Java面试核心知识原理+框架

Java~~~

Java 程序员 面试 编程语言 架构师

SpringCloud Alibaba微服务实战九 - Seata 容器化

AI乔治

Java 架构 微服务 Spring Cloud

耗子尾汁,你居然还不懂什么是架构师?那你编码为了什么?还不看阿里人怎么判定吗?

小Q

Java 学习 编程 架构 面试

想了解物联网应用的自动部署,看这篇就够了

华为云开发者联盟

服务器 华为云 部署

一文带你读懂!华为云在ACMUG技术沙龙上都透露了些啥?

华为云开发者联盟

数据库 大数据 数据

一次带你全面解析Nginx,从安装JDK开始讲起,收藏当手册

996小迁

Java 学习 编程 架构 面试

Docker基础与实战,看这一篇就够了

AI乔治

Java Docker spring 架构

智慧公安二维码报警定位系统,高速路二维码定位报警开发

13530558032

SpringCloud Alibaba微服务实战一基础环境准备

AI乔治

Java 架构 微服务 Spring Cloud

SpringCloud Alibaba微服务实战五 - 限流熔断

AI乔治

Java 架构 微服务 Spring Cloud

区块链农产品溯源解决方案,农产品追溯系统价格

13530558032

字节跳动的这份《算法中文手册》火了,完整版PDF开放下载!不少小伙伴靠这份指南成功掌握了算法的核心技能,成功拿到了 BATJ等大厂offer。

Java架构之路

Java 程序员 架构 面试 编程语言

奉劝各位准备面试的Java程序员耗子尾汁赶紧扔掉网上那些千篇一律的面试题,这份《写给大忙人看的Java核心技术》能够让你快速复习

Java架构之路

Java 程序员 架构 面试 编程语言

SpringCloud Alibaba微服务实战十 - 服务网关SpringCloud Gateway

AI乔治

Java 架构 微服务 Spring Cloud

SpringCloud Alibaba微服务实战二 - 服务注册

AI乔治

Java 架构 微服务 Spring Cloud

SpringCloud Alibaba微服务实战六 - 配置隔离

AI乔治

Java 架构 微服务 Spring Cloud

字节跳动总监总结的开发笔记火了!在知乎上已超5000赞!

Java架构师迁哥

区块链的常识之,什么是区块链股份授权证明机制DPoS?

CECBC

区块链 共识机制

数据库:我没有带闪,不讲武德

比伯

Java 编程 程序员 面试 计算机

对于CRM之于现代化企业的影响以及作用的分析

Learun

敏捷开发 CRM 客户关系管理

Silverlight的多线程能力(上)_Java_吴磊_InfoQ精选文章