C#中的AutoResetEvent和ManualResetEvent用于实现线程同步。其基本工作原理是多个线程持有同一个XXXResetEvent,在这个XXXResetEvent未被set前,各线程都在WaitOne()除挂起;在这个XXXResetEvent被set后,所有被挂起的线程中有一个(AutoResetEvent的情况下)或全部(ManualResetEvent的情况下)恢复执行。
AutoResetEvent与ManualResetEvent的差别在于某个线程在WaitOne()被挂起后重新获得执行权时,是否自动reset这个事件(Event),前者是自动reset的,后者不是。所以从这个角度上也可以解释上段提到的“在这个XXXResetEvent被set后,所有被挂起的线程中有一个(AutoResetEvent的情况下)或全部(ManualResetEvent的情况下)恢复执行”——因为前者一旦被某个线程获取后,立即自动reset这个事件(Event),所以其他持有前者的线程之后WaitOne()时又被挂起;而后者被某个获取后,不会自动reset这个事件(Event),所以后续持有后者的线程在WaitOne()时不会被挂起。
namespace AutoResetEvent_Examples {
class MyMainClass {
/*
* 构造方法的参数设置成false后,表示创建一个没有被set的AutoResetEvent
* 这就导致所有持有这个AutoResetEvent的线程都会在WaitOne()处挂起
* 此时如果挂起的线程数比较多,那么你看一下自己的内存使用量……。
* 如果将参数设置成true,表示创建一个被set的AutoResetEvent
* 持有这个AutoResetEvent的线程们会竞争这个Event
* 此时,在其他条件满足的情况下
* 至少会有一个线程得到执行
* 而不是因得不到Event而导致所有线程都得不到执行
*/
static AutoResetEvent myResetEvent = new AutoResetEvent(false)
static int _Count = 0
static void Main() {
Thread myThread = null
for(int i = 0i <100i++) {
myThread = new Thread(new ThreadStart(MyThreadProc))
myThread.Name = \"Thread\" + i
myThread.Start()
}
myResetEvent.Set()
Console.Read()
}
static void MyThreadProc() {
myResetEvent.WaitOne()
_Count++
Console.WriteLine(\"In thread:{0},label={1}.\",Thread.CurrentThread.Name,_Count)
myResetEvent.Set()
}
}
}
namespace ManualResetEvent_Examples {
class MyMainClass {
/*
* 构造方法的参数设置成false后,表示创建一个没有被set的ManualResetEvent
* 这就导致所有持有这个ManualResetEvent的线程都会在WaitOne()处挂起
* 此时如果挂起的线程数比较多,那么你看一下自己的内存使用量……。
* 如果将参数设置成true,表示创建一个被set的ManualResetEvent
* 持有这个ManualResetEvent的线程们在其他条件满足的情况下
* 会同时得到执行(注意,是同时得到执行!所以本例中的_Count的结果一般是不正确的^_^)
* 而不是因得不到Event而导致所有线程都得不到执行
*/
static ManualResetEvent myResetEvent = new ManualResetEvent(false)
static int _Count = 0
static void Main() {
Thread myThread = null
for(int i = 0i <1000i++) {
myThread = new Thread(new ThreadStart(MyThreadProc))
myThread.Name = \"Thread\" + i
myThread.Start()
}
myResetEvent.Set()
Console.Read()
}
static void MyThreadProc() {
myResetEvent.WaitOne()
_Count++
/*
* 在new ManualResetEvent(false)的情况下
* 下面的输出结果可能比较诡异:多个线程都输出label=1000!
* 一种可能的原因是多个线程在各自执行到_Count++后,被挂起
* 随后打印的_Count值就不是本线程中刚刚修改过的_Count值了。
*/
Console.WriteLine(\"In thread:{0},_Count={1}.\",Thread.CurrentThread.Name,_Count)
}
}
}
set是让事件(Event)发生,而reset是让事件(Event)复位或者说忽略已经的事件(Event)。WaitOne是等待事件(Event)的发生,之后继续向下执行,否则一直等待。
在构造AutoResetEvent和ManualResetEvent的时候,它们的构造方法里需要一个参数initState,中文版MSDN(2005和2008)上的解释是“若要将初始状态设置为终止,则为 true;若要将初始状态设置为非终止,则为false。”,我看了一个下午,没弄明白,而看一下英文版后大概就明白了“A value that you set totrueto set the initial state of the specified event to signaled. Set this value tofalseto set the initial state of the event to nonsignaled.”(参见:http://msdn.microsoft.com/en-us/library/ee432364.aspx),大体意思是说这个参数决定是否在构造这个Event的时候就设置它为“发生”状态(signaled),如果是,则设置为true,也就是说持有这个Event的一个或多个线程在一开始就可以执行,而不需要挂起,至少是不会全部挂起(持有AutoResetEvent的一个或多个线程在任意时刻至多有一个线程在执行;持有ManualResetEvent的一个或多个线程会同时执行),否则为false(持有AutoResetEvent和ManualResetEvent的所有线程都将挂起,因为事件(Event)没有被set,即事件没有发生)。
另外稍微提一下,我在做多线程测试的时候,发现在线程数少的情况下,即使多个线程不做任何同步,如果对一个公共变量进行非互斥式修改时,不会至少很难出现不一致的情况,比如开100个线程,这个线程不做任何同步就分别给一个公共变量执行加1操作,那么结果在绝绝绝大部分的情况下是100!所以,我最后就下了狠心,把线程数增加到1000个,这个时候才出现问题,但问题也不是想象得那么严重——结果在991-1000之间!
再有,MSDN上对Monitor的Wait和Pulse两个方法用法的举例会导致死锁,一种死锁的执行顺序是:
1、线程tSecond在SecondThread()中执行到while(Monitor.Wait(m_smplQueue,1000))后,释放m_smplQueue的锁,线程tSecond挂起;
2、线程tFirst在FirstThread()中执行到Monitor.Wait(m_smplQueue)之前耗费的时间超过1000毫秒,此时线程tSecond退出,线程tFirst挂起,并且从此以后不会被恢复!
可以使用如下改动过的代码验证:
public void FirstThread() {
int counter = 0
lock(m_smplQueue) {
Console.WriteLine(\"11\")
while(counter <MAX_LOOP_TIME) {
//Wait, if the queue is busy.
Console.WriteLine(\"12\")
Monitor.Wait(m_smplQueue)
Console.WriteLine(\"13\")
//Push one element.
m_smplQueue.Enqueue(counter)
Console.WriteLine(\"14\")
//Release the waiting thread.
Monitor.Pulse(m_smplQueue)
Console.WriteLine(\"15\")
counter++
Console.WriteLine(\"16\")
}
}
}
public void SecondThread() {
lock(m_smplQueue) {
Console.WriteLine(\"21\")
//Release the waiting thread.
Monitor.Pulse(m_smplQueue)
Console.WriteLine(\"22\")
//Wait in the loop, while the queue is busy.
//Exit on the time-out when the first thread stops.
while(Monitor.Wait(m_smplQueue,1000)) {
Console.WriteLine(\"23\")
//Pop the first element.
int counter = (int) m_smplQueue.Dequeue()
Console.WriteLine(\"24\")
//Print the first element.
Console.WriteLine(counter.ToString())
Console.WriteLine(\"25\")
//Release the waiting thread.
Monitor.Pulse(m_smplQueue)
Console.WriteLine(\"26\")
}
Console.WriteLine(\"27\")
}
}
C# AutoResetEvent的使用
使用ManualResetEvent.Reset()方法能将ManualResetEvent设置为nonsignaled,未发出信号状态。此时,所有内部代码包含有“ManualResetEvent.waitOne”这句的线程,都会在运行到“ManualResetEvent.waitOne”这句时,发生阻塞,就是停止不动了。直到ManualResetEvent被ManualResetEvent.Set()方法设置为signaled,即变成发出信号状态。阻塞就会消失,那么所有线程都可以越过“ManualResetEvent.waitOne”这句,继续去执行下面的代码。这个类的作用就是去控制其他线程的暂停与继续执行的。
(之前写反了,没注意,现在看到评论,就改好了,谢谢评论的提醒)
如何判断manualresetevent处于终止状态
C# AutoResetEvent的使用
AutoResetEvent 允许线程通过发信号互相通信。通常,此通信涉及线程需要独占访问的资源。线程通过调用 AutoResetEvent 上的 WaitOne 来等待信号。如果 AutoResetEvent 处于非终止状态,则该线程阻塞,并等待当前控制资源的线程
通过调用 Set 发出资源可用的信号。
调用 Set 向 AutoResetEvent 发信号以释放等待线程。AutoResetEvent 将保持终止状态,直到一个正在等待的.线程被释放,然后自动返回非终止状态。如果没有任何线程在等待,则状态将无限期地保持为终止状态。
可以通过将一个布尔值传递给构造函数来控制 AutoResetEvent 的初始状态,如果初始状态为终止状态,则为 true否则为 false。
通俗的来讲只有等myResetEven.Set()成功运行后,myResetEven.WaitOne()才能够获得运行机会Set是发信号,WaitOne是等待信号,只有发了信号,
等待的才会执行。如果不发的话,WaitOne后面的程序就永远不会执行。下面我们来举一个例子:我去书店买书,当我选中一本书后我会去收费处付钱,
付好钱后再去仓库取书。这个顺序不能颠倒,我作为主线程,收费处和仓库做两个辅助线程,代码如下:
using System
using System.Linq
using System.Activities
using System.Activities.Statements
using System.Threading
namespace CaryAREDemo
{
class Me
{
const int numIterations = 550
static AutoResetEvent myResetEvent = new AutoResetEvent(false)
static AutoResetEvent ChangeEvent = new AutoResetEvent(false)
//static ManualResetEvent myResetEvent = new ManualResetEvent(false)
//static ManualResetEvent ChangeEvent = new ManualResetEvent(false)
static int number//这是关键资源
static void Main()
{
Thread payMoneyThread = new Thread(new ThreadStart(PayMoneyProc))
payMoneyThread.Name = \"付钱线程\"
Thread getBookThread = new Thread(new ThreadStart(GetBookProc))
getBookThread.Name = \"取书线程\"
payMoneyThread.Start()
getBookThread.Start()
for (int i = 1i <= numIterationsi++)
{
Console.WriteLine(\"买书线程:数量{0}\", i)
number = i
//Signal that a value has been written.
myResetEvent.Set()
ChangeEvent.Set()
Thread.Sleep(0)
}
payMoneyThread.Abort()
getBookThread.Abort()
}
static void PayMoneyProc()
{
while (true)
{
myResetEvent.WaitOne()
//myResetEvent.Reset()
Console.WriteLine(\"{0}:数量{1}\", Thread.CurrentThread.Name, number)
}
}
static void GetBookProc()
{
while (true)
{
ChangeEvent.WaitOne()
// ChangeEvent.Reset()
Console.WriteLine(\"{0}:数量{1}\", Thread.CurrentThread.Name, number)
Console.WriteLine(\"------------------------------------------\")
Thread.Sleep(0)
}
}
}
}
运行结果如下:
AutoResetEvent与ManualResetEvent的区别
他们的用法\\声明都很类似,Set方法将信号置为发送状态 Reset方法将信号置为不发送状态WaitOne等待信号的发送。其实,从名字就可以看出一个手动,
一个自动,这个手动和自动实际指的是在Reset方法的处理上,如下面例子:
public AutoResetEvent autoevent=new AutoResetEvent(true)
public ManualResetEvent manualevent=new ManualResetEvent(true)
默认信号都处于发送状态,
autoevent.WaitOne()
manualevent.WaitOne()
如果 某个线程调用上面该方法,则当信号处于发送状态时,该线程会得到信号,得以继续执行。差别就在调用后,autoevent.WaitOne()每次只允许一个线程
进入,当某个线程得到信号(也就是有其他线程调用了autoevent.Set()方法后)后,autoevent会自动又将信号置为不发送状态,则其他调用WaitOne的线程只
有继续等待.也就是说,autoevent一次只唤醒一个线程。而manualevent则可以唤醒多个线程,因为当某个线程调用了set方法后,其他调用waitone的线程
获得信号得以继续执行,而manualevent不会自动将信号置为不发送.也就是说,除非手工调用了manualevent.Reset().方法,则manualevent将一直保持有信号状态,manualevent也就可以同时唤醒多个线程继续执行。如果上面的程序换成ManualResetEvent的话,就需要在waitone后面做下reset。
AutoResetEvent.Set() 这个方法的问题,请专业人士回答
使用ManualResetEvent.set()方法能将ManualResetEvent设置为fasle,非终止状态,此时,所有内部代码包含有“ManualResetEvent.waitOne”这句的线程,都会在运行到“ManualResetEvent.waitOne”这句时,发生阻塞,就是停止不动了。直到ManualResetEvent被ManualResetEvent.reset()方法设置为true,即变成终止状态。阻塞就会消失,那么所有线程都可以越过“ManualResetEvent.waitOne”这句,继续去执行下面的代码。这个类的作用就是去控制其他线程的暂停与继续执行的。
事件是static AutoResetEvent myResetEvent = new AutoResetEvent(false)这一行定义的。
AutoResetEvent 自动回调的事件吧,每当满足某种条件就自动在另个线程执行一段代码,“状态设置为终止状态”就是说当已经满足特定条件的情况下,myResetEvent 这个事件也不自动执行。
以上就是关于ManualResetEvent和AutoResetEvent的区别全部的内容,如果了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!