博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
在Silverlight程序中使用Thread一个很容易被忽略的问题
阅读量:6222 次
发布时间:2019-06-21

本文共 4994 字,大约阅读时间需要 16 分钟。

有一个很常见的功能,我们需要在一个子窗口中定时调用服务,然后更新控件的内容,只要窗口开着就一直会调用服务。

那么现在就来完成这个功能,首先定义一个服务:

public class Service1 : IService1    {        public string DoWork(string name)        {            File.AppendAllText(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "log.txt"), string.Format("时间:{0} 线程:{1}" + Environment.NewLine, DateTime.Now, name));            return "OK";        }    }

这个服务在返回数据之前会写一个本地文本日志,写入时间和传入的参数。

然后添加一个子窗口,并在页面上放置一个按钮打开这个窗口:

ChildWindow1 c = new ChildWindow1();            c.Show();

子窗口的代码如下:

public partial class ChildWindow1 : ChildWindow    {        private Thread thread;        private static int threadNum = 0;        private ServiceReference1.Service1Client s = new ServiceReference1.Service1Client();        public ChildWindow1()        {            InitializeComponent();            s.DoWorkCompleted += new EventHandler
(s_DoWorkCompleted); } private void s_DoWorkCompleted(object sender, ServiceReference1.DoWorkCompletedEventArgs e) { Dispatcher.BeginInvoke(() => Message.Items.Insert(0, string.Format("服务返回:{0} 控件:{1} 时间:{2} 线程:{3}", e.Result, Message.GetHashCode(), DateTime.Now, e.UserState.ToString()))); } private void ChildWindow_Loaded(object sender, RoutedEventArgs e) { Interlocked.Increment(ref threadNum); thread = new Thread(state => { var threadID = (int)state; while (true) { s.DoWorkAsync(threadID.ToString(), threadID); Thread.Sleep(1000); } }); thread.Start(threadNum); }

这个代码很简单:

1、我们有一个静态变量保存了总共的线程数

2、每次窗口打开+1

3、每次窗口打开的时候初始化一个线程,这个线程的作用就是不断调用服务,然后休眠1秒,调用的时候传入的参数就是当前线程编号(从1开始)

4、服务调用完成之后,我们会更新页面上的ListBox,添加一条数据,内容包括服务返回值、Message控件的标识、当前时间和后台线程的编号

好了,功能完成了,运行程序,我们的期望是在子窗口打开后:

1、子窗口的ListBox每一秒都会插入一行记录

2、服务端每一秒都会往文本文件写入一行记录

3、两者的线程编号保持一致,并且每次打开子窗口线程编号都会+1

 

运行程序,打开子窗口:

看看服务端的日志:

貌似完成了我们的需求,这个程序真的没问题吗?

第二次打开子窗口:

也没问题,但是服务端的日志就不对了(看到没问题不代表真的没问题啊):

这说明两个后台线程同时在运行,不能想当然认为子窗口关闭了,老的线程会结束(可能会觉得反正不是静态字段嘛,窗口关闭了等它回收Thread去)。

Silverlight的程序一般很少刷新页面,随着子窗口开关越来越多,线程也越来越多,并且以前的线程会一直进行服务调用。

随着子窗口开关无数次,后台有无数个线程在调用服务端,想想也是很恐怖的事情!

前端显示一点问题都没有(为什么ListBox却没有多显示?大家可以思考一下),潜在增加了服务端的压力,我们希望的是再子窗口关闭之后,后台线程可以结束。

很多人可能会这么写:

private void ChildWindow_Closed(object sender, EventArgs e)        {            if (thread != null)            {                try                {                    thread.Abort();                }                catch                {                }            }        }

子窗口关闭的时候结束线程嘛,反正它会抛ThreadAbortException,不用管了。

开关几次子窗口看看:

还是一样,难道线程没结束?

想当然了!其实何必强制结束线程呢?让线程自己完成就可以了:

这样就正常了。当然,也可以使用BackgroundWorker来实现这个功能:

using System;using System.Collections.Generic;using System.Linq;using System.Net;using System.Windows;using System.Windows.Controls;using System.Windows.Documents;using System.Windows.Input;using System.Windows.Media;using System.Windows.Media.Animation;using System.Windows.Shapes;using System.Threading;using System.ComponentModel;namespace SilverlightApplication2{    public partial class ChildWindow1 : ChildWindow    {        private static int threadNum = 0;        private ServiceReference1.Service1Client s = new ServiceReference1.Service1Client();        private BackgroundWorker bw = new BackgroundWorker();        public ChildWindow1()        {            InitializeComponent();            s.DoWorkCompleted += new EventHandler
(s_DoWorkCompleted); bw.WorkerSupportsCancellation = true; bw.WorkerReportsProgress = true; bw.DoWork += new DoWorkEventHandler(bw_DoWork); bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged); } private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e) { Message.Items.Insert(0, string.Format("服务器返回:{0} 控件:{1} 时间:{2} 线程:{3}", e.UserState, Message.GetHashCode(), DateTime.Now, e.UserState.ToString())); } private void s_DoWorkCompleted(object sender, ServiceReference1.DoWorkCompletedEventArgs e) { bw.ReportProgress(0, e.Result); } private void bw_DoWork(object sender, DoWorkEventArgs e) { while (!bw.CancellationPending) { var threadID = (int)e.Argument; s.DoWorkAsync(threadID.ToString(), threadID); Thread.Sleep(1000); } } private void ChildWindow_Loaded(object sender, RoutedEventArgs e) { Interlocked.Increment(ref threadNum); bw.RunWorkerAsync(threadNum); } private void ChildWindow_Closed(object sender, EventArgs e) { bw.CancelAsync(); } }}

当然,本文标题虽然说Silverlight,对于Winform也是一样的,只不过Silverlight的Thread.Abort()不奏效。总之两点,一,有的时候不能太想当然和凭经验,只有实际验证了才知道结果;二,写.NET程序也不能啥都不释放不去管,对于线程、网络链接等资源、大量内存分配还是需要多关注。

转载地址:http://pxrja.baihongyu.com/

你可能感兴趣的文章
Java设计模式--责任链模式
查看>>
Zenefits CodeSprint:Knight or Knave
查看>>
网络通信协议、UDP与TCP协议、UDP通信、TCP通信
查看>>
Ogg - 从oracle到mysql的同步
查看>>
js中判断对象类型的几种方法
查看>>
grep多条件和sed合并两行
查看>>
iOS 之 时间格式与字符串转换
查看>>
js导出CSV
查看>>
转:Linux中find命令-path -prune用法详解
查看>>
团队编程项目作业3-模块测试过程
查看>>
Java基本数据类型及其封装器的一些千丝万缕的纠葛
查看>>
easyui datagrid 分页 客户分页
查看>>
ogg概叙、架构、进程
查看>>
建造者模式的使用场景
查看>>
java基本类型
查看>>
iReport报表生成html,pdf,xls,word工具类
查看>>
转一篇关于部署的文章
查看>>
cvc-complex-type.2.4.c 如何解决
查看>>
如何优化VMWare虚拟机的运行速度(转)
查看>>
UVALive2362 POJ1004 HDU1064 ZOJ1048 Financial Management【数学计算】
查看>>