ManualResetEvent用法

  http://blog.tom.com/blog/read.php?bloggerid=313638&blogid=13505
Thread and Sync In C# (C#中的线程与同步)
别相信别人告诉你的所有的事。其实C#中的线程是很简单的。
线程是程序中的控制流程的封装。你可能已经习惯于写单线程程序,也就是,程序在它们的代码中一次只在一条路中执行。如果你多弄几个线程的话,代码运行可能会更加“同步”。在一个有着多线程的典型进程中,零个或更多线程在同时运行。但是,在有着N个CPU的机器上,一个线程只能在给定的时间上在一个CPU上运行,因为每个线程都是一个代码段,每个CPU一次只能运行一段代码。而看起来像是N个同时完成是线程间共享CPU时间片的效果。这个例子里,我们将创建另一个线程,我们将用两个线程演示多线程的工作方式,最后,我们实现两个线程(主线程与新线程)同步,在新线程工作前必须等待消息。建立线程前我们必须引入System.Threading命名空间。然后我需要知道的是,线程得为控制流程建立一个起点。起点是一个函数,可以使一个相同的调用或其它。
这里你可以看到在同一个类中定义的起点函数。
None.gif using  System;
None.gif
using  System.Threading;
None.gif
namespace  ThreadingTester
ExpandedBlockStart.gif
{
InBlock.gif
class ThreadClass
ExpandedSubBlockStart.gif
{
InBlock.gif  
public static void trmain()
ExpandedSubBlockStart.gif  
{
InBlock.gif    
for(int x=0;x < 10;x++)
ExpandedSubBlockStart.gif    
{
InBlock.gif    Thread.Sleep(
1000);
InBlock.gif    Console.WriteLine(x);
ExpandedSubBlockEnd.gif    }

ExpandedSubBlockEnd.gif  }
  
InBlock.gif  
static void Main(string[] args)
ExpandedSubBlockStart.gif  
{
InBlock.gif    Thread thrd1
=new Thread(new ThreadStart(trmain));
InBlock.gif    thrd1.Start();
InBlock.gif    
for(int x=0;x < 10;x++
ExpandedSubBlockStart.gif    
{
InBlock.gif    Thread.Sleep(
900);
InBlock.gif    Console.WriteLine(
"Main    :" + x);
ExpandedSubBlockEnd.gif    }

ExpandedSubBlockEnd.gif  }

ExpandedSubBlockEnd.gif}

ExpandedBlockEnd.gif}

None.gif

Thread.Sleep(n)方法把“this”线程置于n毫秒的休眠状态。你可以看看这个例子,在主函数我们定义了一个新的线程,其中它的起点是函数trmain(),我们然后包含了Start()方法开始执行。如果你运行这个例子,你就会了解线程间的切换(让CPU从运行一个线程转到另一个线程)让线程几乎同时运行,为了能看哪个线程运行更快我把主线程设置比新线程少100毫秒。
现在,在开始线程前,先给线程命名:
  Thread thrd1=new Thread(new ThreadStart(trmain));
  thrd1.Name="thread1";
  thrd1.Start();
  Thread tr = Thread.CurrentThread;
  Console.WriteLine(tr.Name);
在完成上面程序后,设想我们不想在一开始新线程就让它马上运行结束,也就是说,我们开启了一个新线程,让它运行,在某个特定的时间点,新线程暂停并等待从主线程(或其他线程)发来的消息。
我们可以这样定义:
  public static ManualResetEvent mre = new ManualResetEvent(false);
ManualResetEvent建立时是把false作为start的初始状态,这个类用于通知另一个线程,让它等待一个或多个线程。注意,为了通知或监听同一个线程,所有的其它线程都能访问那个类。
等待线程这样写:
  mre.WaitOne();
这将引起等待线程无限期的阻塞并等待类来通知。
发信号的线程应该这样:
  mre.Set();
这样类就会被通知,值变成true,等待线程就会停止等待。在通知事件发生后,我们就可以使用下面语句把线程置于基状态:
  mre.Reset();
现在让我们在程序执行一下:
None.gif using  System;
None.gif
using  System.Threading;
None.gif
namespace  ThreadingTester
ExpandedBlockStart.gif
{
InBlock.gif
class ThreadClass
ExpandedSubBlockStart.gif
{
InBlock.gif
public static ManualResetEvent mre=new ManualResetEvent(false);
InBlock.gif
public static void trmain()
ExpandedSubBlockStart.gif
{
InBlock.gifThread tr 
= Thread.CurrentThread;
InBlock.gifConsole.WriteLine(
"thread: waiting for an event");
InBlock.gifmre.WaitOne();
InBlock.gifConsole.WriteLine(
"thread: got an event");
InBlock.gif
for(int x=0;x < 10;x++)
ExpandedSubBlockStart.gif
{
InBlock.gifThread.Sleep(
1000);
InBlock.gifConsole.WriteLine(tr.Name 
+"" + x);
ExpandedSubBlockEnd.gif}

ExpandedSubBlockEnd.gif}
  
InBlock.gif
static void Main(string[] args)
InBlock.gif
ExpandedSubBlockStart.gif
{
InBlock.gifThread thrd1
=new Thread(new ThreadStart(trmain));
InBlock.gifthrd1.Name
="thread1";
InBlock.gifthrd1.Start();
InBlock.gif
for(int x=0;x < 10;x++
ExpandedSubBlockStart.gif
{
InBlock.gifThread.Sleep(
900);
InBlock.gifConsole.WriteLine(
"Main:" + x);
InBlock.gif
if(5==x) mre.Set();
ExpandedSubBlockEnd.gif}

InBlock.gif
while(thrd1.IsAlive)
ExpandedSubBlockStart.gif
{
InBlock.gifThread.Sleep(
1000);
InBlock.gifConsole.WriteLine(
"Main: waiting for thread to stopdot.gif");
ExpandedSubBlockEnd.gif}

ExpandedSubBlockEnd.gif}

ExpandedSubBlockEnd.gif}

ExpandedBlockEnd.gif}

 

两者都继承自EventWaitHandle,自然也就有点相通的地方了

 

简单来说,无论是AutoResetEvent还是ManualResetEvent,都是通过发出Signal信号来通知正在等待的线程的。有人可能要问,为什么要用它们来做这些事情, 嗯, 是这样的, 在.Net的多线程环境中,资源的共享变得尤其重要,如果没有一个有效的方法来维护资源的原子状态,在抢占式的CPU环境中,所有的事情都会变得无法控制。AutoResetEvent和ManualResetEvent正是用来保证资源的原子性的一个手段的两个方面。正如它们的名字一样, AutoResetEvent会在每次被Signal了之后自动(Automatically)转变为UnSignal,而ManualResetEvent则不然,无论是Signal还是UnSignal都需要人为的介入去改变它的状态。

 

在AutoResetEvent的构造函数中,有唯一的一个参数,initialState,Boolean类型,它用来初始化AutoResetEvent的Signal状态,True为Signal,False为UnSignal,这与ManualResetEvent的构造函数是一样的。

 

AutoResetEvent或ManualResetEvent都是通过Set()/Reset()两个方法来Signal/UnSignal信号,通过调用WaitOne方法来阻塞当前线程,当收到Signal后就继续往下执行。上面提到过,AutoResetEvent会自动把信号复位(自动调用Reset),而ManualResetEvent则需要人手复位,也就是说,AutoResetEvent每次只允许一条线程进入,其它所有需要访问该资源的线程都要排队等候,直到AutoResetEvent得到信号后,下一条线程开始工作,同时AutoResetEvent又会自动复位信号,让其他线程继续等候;而ManualResetEvent则每次可以唤醒多个线程,因为当ManualResetEvent得到信号后,其他调用WaitOne方法的线程都将得到信号得以继续往下执行,ManualResetEvent不会自动复位信号,换句话说,除非手动的调用了ManualResetEvent.Reset方法,否则ManualResetEvent一直保持有信号状态,这样就可以同时唤醒多条线程了

 

作为一个示例,下面的DemoCode一开始就运行一条子线程,然后用AutoResetEvent/ManualResetEvent来控制MessageBox的输出流程。

如果在Foo和button1_Click里面用AutoResetEvent,一开始得到"1",然后每点击一次Button1得到"2"和"3"。

如果用ManualResetEvent,一开始得到"1",然后只需点击Button1一次,就可以得到"2"和"3"了

 

你可能感兴趣的:(C#)