ManualResetEvent用法「建议收藏」

ManualResetEvent用法「建议收藏」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
ContractedBlock.gif
dot.gif
{

InBlock.gifclass ThreadClass
ExpandedSubBlockStart.gifContractedSubBlock.gifdot.gif{

InBlock.gif  public static void trmain()
ExpandedSubBlockStart.gifContractedSubBlock.gif  dot.gif{

InBlock.gif    for(int x=0;x < 10;x++)
ExpandedSubBlockStart.gifContractedSubBlock.gif    dot.gif{

InBlock.gif    Thread.Sleep(1000);
InBlock.gif    Console.WriteLine(x);
ExpandedSubBlockEnd.gif    }

ExpandedSubBlockEnd.gif  }
  
InBlock.gif  static void Main(string[] args)
ExpandedSubBlockStart.gifContractedSubBlock.gif  dot.gif{

InBlock.gif    Thread thrd1=new Thread(new ThreadStart(trmain));
InBlock.gif    thrd1.Start();
InBlock.gif    for(int x=0;x < 10;x++) 
ExpandedSubBlockStart.gifContractedSubBlock.gif    dot.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
ContractedBlock.gif
dot.gif
{

InBlock.gifclass ThreadClass
ExpandedSubBlockStart.gifContractedSubBlock.gifdot.gif{

InBlock.gifpublic static ManualResetEvent mre=new ManualResetEvent(false);
InBlock.gifpublic static void trmain()
ExpandedSubBlockStart.gifContractedSubBlock.gifdot.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.giffor(int x=0;x < 10;x++)
ExpandedSubBlockStart.gifContractedSubBlock.gifdot.gif{

InBlock.gifThread.Sleep(1000);
InBlock.gifConsole.WriteLine(tr.Name +”: ” + x);
ExpandedSubBlockEnd.gif}

ExpandedSubBlockEnd.gif}
  
InBlock.gifstatic void Main(string[] args)
InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gifdot.gif{

InBlock.gifThread thrd1=new Thread(new ThreadStart(trmain));
InBlock.gifthrd1.Name=”thread1″;
InBlock.gifthrd1.Start();
InBlock.giffor(int x=0;x < 10;x++) 
ExpandedSubBlockStart.gifContractedSubBlock.gifdot.gif{

InBlock.gifThread.Sleep(900);
InBlock.gifConsole.WriteLine(“Main:” + x);
InBlock.gifif(5==x) mre.Set();
ExpandedSubBlockEnd.gif}

InBlock.gifwhile(thrd1.IsAlive)
ExpandedSubBlockStart.gifContractedSubBlock.gifdot.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″了

 

           

再分享一下我老师大神的人工智能教程吧。零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!https://blog.csdn.net/jiangjunshow

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/158564.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号