您的位置:首页 > 编程语言 > C#

C#笔记20:多线程之线程同步中的信号量AutoResetEvent和ManualResetEvent

2010-09-17 15:24 591 查看
C#笔记20:多线程之线程同步中的信号量AutoResetEvent和ManualResetEvent

本章概要:

1:终止状态和非终止状态

2:AutoResetEvent和ManualResetEvent的区别

3:WaitHandle.WaitOne()等

1:终止状态和非终止状态

首先说说线程的终止状态和非终止状态。AutoResetEvent和ManualResetEvent的构造函数中,都有bool变量来指明线程的终止状态和非终止状态。true表示终止状态,false表示非终止状态。看代码片段1:

代码片段1:

AutoResetEvent _autoResetEvent = new AutoResetEvent(false);

private void BT_Temp_Click(object sender, RoutedEventArgs e)
{
Thread t1 = new Thread(this.Thread1Foo);
t1.Start();
Thread.Sleep(3000);
_autoResetEvent.Set();
}

void Thread1Foo()
{
_autoResetEvent.WaitOne();
MessageBox.Show("t1 end");
}


这段代码的执行结果,就是3秒钟过后,弹出“t1 end”。
而如果把:
AutoResetEvent _autoResetEvent = new AutoResetEvent(false);
改为:
AutoResetEvent _autoResetEvent = new AutoResetEvent(true);
则“t1 end”将会立刻弹出。
也就是说,在终止状态中,_autoResetEvent.WaitOne()是不会起到阻滞工作线程的作用的。(PS:ManualResetEvent也同样)

二:AutoResetEvent和ManualResetEvent的区别
接下来,再来看看AutoResetEvent和ManualResetEvent的区别。我们看代码段2和代码段3:

代码段2:
AutoResetEvent _autoResetEvent = new AutoResetEvent(false);
private void BT_Temp_Click(object sender, RoutedEventArgs e)
{
Thread t1 = new Thread(this.Thread1Foo);
t1.Start();
Thread t2 = new Thread(this.Thread2Foo);
t2.Start();
Thread.Sleep(3000);
_autoResetEvent.Set();
}

void Thread1Foo()
{
_autoResetEvent.WaitOne();
MessageBox.Show("t1 end");
}

void Thread2Foo()
{
_autoResetEvent.WaitOne();
MessageBox.Show("t2 end");
}

这段代码的执行结果,就是3秒钟过后,弹出“t1 end”。
而如果把:
AutoResetEvent _autoResetEvent = new AutoResetEvent(false);
改为:
AutoResetEvent _autoResetEvent = new AutoResetEvent(true);
则“t1 end”将会立刻弹出。
也就是说,在终止状态中,_autoResetEvent.WaitOne()是不会起到阻滞工作线程的作用的。(PS:ManualResetEvent也同样)

2:AutoResetEvent和ManualResetEvent的区别

接下来,再来看看AutoResetEvent和ManualResetEvent的区别。我们看代码段2和代码段3:

代码段2:

AutoResetEvent _autoResetEvent = new AutoResetEvent(false);
private void BT_Temp_Click(object sender, RoutedEventArgs e)
{
Thread t1 = new Thread(this.Thread1Foo);
t1.Start();
Thread t2 = new Thread(this.Thread2Foo);
t2.Start();
Thread.Sleep(3000);
_autoResetEvent.Set();
}

void Thread1Foo()
{
_autoResetEvent.WaitOne();
MessageBox.Show("t1 end");
}

void Thread2Foo()
{
_autoResetEvent.WaitOne();
MessageBox.Show("t2 end");
}


该段代码运行的效果是,过3秒后,要么弹出“t1 end”,要么弹出“t2 end”,不会两个都弹出。也就是说,其中一个进行将会结束,而另一个进程永远不会结束。

代码段3:

ManualResetEvent _menuRestEvent = new ManualResetEvent(false);

private void BT_Temp_Click(object sender, RoutedEventArgs e)
{
Thread t1 = new Thread(this.Thread1Foo);
t1.Start();
Thread t2 = new Thread(this.Thread2Foo);
t2.Start();
Thread.Sleep(3000);
_menuRestEvent.Set();
}

void Thread1Foo()
{
_menuRestEvent.WaitOne();
MessageBox.Show("t1 end");
}

void Thread2Foo()
{
_menuRestEvent.WaitOne();
MessageBox.Show("t2 end");
}


该段代码运行的效果是,过3秒后,“t1 end”和“t2 end”,两个都被弹出。也就是说,两个进程都结束了。
这个特性就是说,AutoResetEvent只会给一个线程发送信号,而不会给多个线程发送信号。在我们需要同步多个线程的时候,就只能采用ManualResetEvent了。至于深层次的原因是,AutoResetEvent在set()之后,会将线程状态自动置为false,而ManualResetEvent在Set()后,线程的状态就变为true了,必须手动ReSet()之后,才会重新将线程置为false。这也就是为什么他们的名字一个为Auto,一个为Manual的原因。为了更加充分的验证ManualResetEvent的这点特性,我们再来看代码片段4

代码片段4:

ManualResetEvent _menuRestEvent = new ManualResetEvent(false);

private void BT_Temp_Click(object sender, RoutedEventArgs e)
{
Thread t1 = new Thread(this.Thread1Foo);
t1.Start();
Thread t2 = new Thread(this.Thread2Foo);
t2.Start();
Thread.Sleep(3000);
_menuRestEvent.Set();
//_menuRestEvent.Reset();
}

void Thread1Foo()
{
_menuRestEvent.WaitOne();
MessageBox.Show("t1 step1 end");
//睡1S,用于等待主线程_menuRestEvent.Reset();
Thread.Sleep(1000);
_menuRestEvent.WaitOne();
MessageBox.Show("t1 step2 end");
}

void Thread2Foo()
{
_menuRestEvent.WaitOne();
MessageBox.Show("t2 step1 end");
//睡1S,用于等待主线程_menuRestEvent.Reset();
Thread.Sleep(1000);
_menuRestEvent.WaitOne();
MessageBox.Show("t2 step2 end");
}


在代码片段4中,我们对//_menuRestEvent.Reset()进行了注释,也就是说, _menuRestEvent.Set()后,线程的状态就是true状态的,程序运行的结果是"t1 step1 end"、"t1 step2 end"、"t1 step2 end"、"t2 step2 end"在3秒之后全部弹出。
而如果我们将//_menuRestEvent.Reset()的注释去掉,会发现"t1 step2 end"和"t2 step2 end"永远不会弹出。除非我们在主线程中再次对_menuRestEvent进行Set()。

3:WaitHandle.WaitOne()等

无论是用什么方法启动一个新线程,我们都可以得到这个线程的WaitHandle,而使用WaitOne等方法的作用就是组织当前线程,直到有其它线程给当前线程发送信号。

我们来看看一个很能说明该问题的例子,该例子的描述如下:

模拟通信中的客户端。客户端在运行过程中,服务器每隔一段的时间会给客户端发送心跳数据。客户端启动一个线程,每3秒检测是否收到心跳数据,如果没有心跳数据,则告诉自己已经和服务器断开连接。代码如下:

Thread t;
AutoResetEvent _autoResetEvent = new AutoResetEvent(false);

private void button1_Click(object sender, EventArgs e)
{
t = new Thread(new ThreadStart(delegate
{
while (true)
{
//等3秒,3秒没有信号,显示断开
//有信号,则显示更新
bool re = _autoResetEvent.WaitOne(3000);
ShowRe(re);
}
}));
t.Start();
t.IsBackground = true;
}

void ShowRe(bool re)
{
label1.BeginInvoke(new MethodInvoker(delegate
{
if (re)
{
label1.Text = DateTime.Now.ToString() + "保持连接状态";
}
else
{
label1.Text = DateTime.Now.ToString() + "断开,需要重启";
}
}));
}

//模拟服务器发送心跳数据。
private void button2_Click(object sender, EventArgs e)
{
_autoResetEvent.Set();
}


TMJ .NET培训,开创 200元/月,学到会!

NET C# 入门级.NET C# 专业级.NET 架构级BS系统专业级BS系统安全
1.开篇及C#程序、解决方案的结构

2.源码管理之TFS入门

3.打老鼠初级

……

21.CMS之主要功能实现

22.进程和线程基础

23.类型转换

24.算法基础

25.初级课程之剩余知识点

1.消灭打老鼠游戏中的自定义委托

2.垃圾回收

3.Dispose模式

……

16.异常使用指导

17.最常用的重构指导

18.Debug和IDE的进阶

19.Resharper的使用

20.ILSPY的使用

1.Socket入门

2.打造打老鼠游戏网络版

3.WCF入门

……

10.依赖注入

11.万物兼可测试

12.软件指标之覆盖率计算

13.软件指标之代码行

14.软件指标之圈复杂度、嵌套深度
1.HTML

2.WebForm原理

3.CSS必知必会

……

19.让浏览器缓存Shop

20.Asp.net的生命周期

21.Asp.net网站的发布以及调试晋级

22.BS程序的本质

23.压力测试我们的Shop

1.Fiddler必知必会

2.IE开发者工具必知必会

3.跨站脚本防范

4.权限欺骗防范

5.参数越界防范

6.会话劫持防范

7.CSRF防范

8.盗链防范

9.静态文件的保护
将本文分享到:
QQ空间
新浪微博
人人网
开心网
搜狐微博
MSN
谷歌
更多
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: