前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >线程间通讯:WaitHandler使用实例及分析

线程间通讯:WaitHandler使用实例及分析

作者头像
^_^肥仔John
发布2018-01-18 11:18:38
5790
发布2018-01-18 11:18:38
举报

实例效果:

1.点击“启动线程”会启动一个线程t每隔2秒在listbox上插入一条新记录。

2.点击“关闭线程”会停止线程t,但不是马上停止而是等待线程t当次循环的工作后再结束。

Form1.cs

代码语言:javascript
复制
 1 using System;
 2 using System.Collections.Generic;
 3 using System.ComponentModel;
 4 using System.Data;
 5 using System.Drawing;
 6 using System.Linq;
 7 using System.Text;
 8 using System.Windows.Forms;
 9 using System.Threading;
10 
11 namespace TestThread
12 {
13     public partial class Form1 : Form
14     {
15         private ManualResetEvent Stop = new ManualResetEvent(false);//用于告诉线程t要关闭t线程
16         private ManualResetEvent Stoped = new ManualResetEvent(false);//用于告诉主线程t线程已关闭
17         Thread t = null;
18         private delegate void SetUIDelegate(string val);//用于线程t操作ui控件
19 
20         public Form1()
21         {
22             InitializeComponent();
23         }
24 
25         private void button1_Click(object sender, EventArgs e)//“启动线程”
26         {
27             MyThread mt = new MyThread(Stop, Stoped);
28             mt.UIEvent += (val) =>
29                 {
30                     if (lbx.InvokeRequired)
31                         lbx.Invoke(new SetUIDelegate(SetUI), val);
32                     else
33                         lbx.Items.Add(val);
34                 };
35             t = new Thread(() =>
36                 {
37                     mt.Run();
38                 });
39             t.Start();
40         }
41 
42         private void button2_Click(object sender, EventArgs e)//“关闭线程”
43         {
44             if (null!=t&&t.IsAlive)//若线程t存在并存活才需关闭 
45             {
46                 Stop.Set();//发出指令告诉线程t:你是时候死了!
47 
48                 while (t.IsAlive)//因线程t不是马上自尽(也许还要吃个包、喝口茶在上吊哦!),所以要继续检查它是否存活
49                 {
50                     if (Stoped.WaitOne(0, false))//阻塞当前线程(这里设置阻塞0秒),就是看看线程t死了没
51                     {
52                         button1.Enabled = false;
53                     }
54                      Application.DoEvents();//因ui线程一直在检查线程t的死活,弄得其他需要ui线程的处理都无法进行,加上这句ui线程就有时间理睬一下其他处理了,以免画面假死!
55                 }
56             }  
57         }
58 
59         private void SetUI(string val)
60         {
61             lbx.Items.Add(val);
62         }
63     }
64 }

MyThread.cs

代码语言:javascript
复制
 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading;
 6 
 7 namespace TestThread
 8 {
 9     public delegate void SetUI(string val);
10  
11     class MyThread
12     {
13         private ManualResetEvent Stop = null;
14         private ManualResetEvent Stoped = null;
15         public event SetUI UIEvent;
16 
17         public MyThread(ManualResetEvent stop,ManualResetEvent stoped)
18         {
19             Stop = stop;
20             Stoped = stoped;
21         }
22 
23         public void Run()
24         {
25             int i = 0;
26             while (true)
27             {
28                 Thread.Sleep(2000);//睡2秒再工作吧!
29                 UIEvent(i.ToString());//操作ui控件
30                 i++;
31                 if (Stop.WaitOne(0, false))//阻塞当前线程(这里又只阻塞0秒),直到ui线程赐死线程t
32                 {
33                     Stoped.Set();//告诉ui线程它要自杀了
34                     break;//自杀去了
35                 }
36             }
37         }
38     }
39 }

子线程之死可以有两种方式:1、ui线程中调用t.Abort(),为它杀,也许线程t还有些事没做但已经没有机会了;2、让线程t退出或调用Thread.CurrentThread.Abort(),为自杀,这样线程t就可以在临死前了结心愿了。而上述功能就属于让线程t自杀,下面进一步分析。

线程t无缘无故是不会自杀的,而ui线程要它自刎就必须发出一条命令,而这条命令就是ManualResetEvent对象。先看一看类结构

可以看到ManualResetEvent类有一个孪生兄弟AutoResetEvent类,它们的祖父是WaitHandle类。

ManualResetEvent实例有终止和非终止两个状态,在初始化时可以设定。它的Set()方法会将实例设为终止状态,Reset()方法会将实例设为非终止状态。而WaitOne()就是阻塞当前线程直到实例被设为终止状态,而WaitOne()方法有多个重载方法,可以设定阻塞时间,超过了阻塞时间实例状态依然为非终止的话就放弃阻塞,让线程继续执行WaitOne语句以下的内容。WaitOne返回值为Boolean值,表示实例状态是否为终止状态。

AutoResetEvent类跟ManualResetEvent类只有一点区别就是它会自动把实例设为非终止状态。

而使用WaitHandle的静态方法WaitAll或WaitAny可以检查多个ManualResetEvent实例和AutoResetEvent实例的状态。

上述代码中线程t自杀时通过另一个ManualResetEvent实例告诉ui线程“我挂了!”,好让ui线程做善后工作。

检查线程t是否已死的过程是一直占用ui线程的,而窗口上控件的交互也是由ui线程来处理,这时会出现画面假死的状态,如果发出了调用ui线程处理其他事件的话就会有异常。这时加上一句Application.DoEvents()表示让处理当前消息队列中的所有window消息,就是说ui线程抽出一部分时间来处理消息队列中的其他消息(如界面的交互),而不是完成了第一个消息再着手后面的消息。注意:这时ui线程是可用的,只是正忙于处理第一个消息,如果ui线程挂起来了、阻塞了或死了Application.DoEvents()无法使让ui线程处理消息队列中的其他消息。

要实现上述的子线程自杀方式也可以用两个静态变量来做控制,至于实现方法我这里就不写了。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2012-01-11 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
数据库
云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档