Wait 和Pulse 等待和暂停的信号
2015-12-04 17:44
525 查看
早期谈论过等待事件句柄(调用Wait的线程在没有收到另一个线程的通知前会一直阻塞)。
Monitor借助它的静态方法Wait,Pulse,PulseAll提供了一个更给力的信号构造,使用这些方法和lock语句,你可以自己实现AutoResetEvent,ManualResetEvent和Semaphore。甚至WaitHandle的WaitAll和WaitAny方法了。
怎样使用Wait
和Pulse
?
1:定义一个同步对象,例如:
Readonly object _locker=new object();
2:定义自己的阻塞条件中的字段。
bool _go 或者 int _semaphoreCount;
3:当你想要阻塞的时候,包含下面的代码
lock(_locker)
while(<阻塞条件 >) //比如while (_go ==false)
Monitor.Wait(_locker); //满足阻塞条件,开始阻塞。
4:当想要改变阻塞条件的时候,包含下面的代码:
lock(_locker)
{
//<更改阻塞条件中的字段>,比如_go=true;
Monitor.Pulse(_locker); //或者: Monitor.PulseAll(_locker); //通知等待队列中的线程锁定对象状态的更改。
}
这个模式可以让你随时随地等待线程。下面是一个例子,worker线程在_go
字段变成true之前会一直等待。
class Program
{
static bool go;
static object locker = new object();
static void Main(string[] args)
{
Thread tr = new Thread(Work);
tr.Start();
Console.ReadLine(); //等待用户输入
lock (locker)
{
go = true; //改变阻塞条件
Monitor.Pulse(locker); //通知等待的队列。
}
Console.ReadKey();
}
static void Work()
{
lock (locker)
{
while (!go)
{
Monitor.Wait(locker);
}
}
Console.WriteLine("Waked");
}
}
}
为了线程安全,确保所有共享的字段在读取的时候都加锁了。
Work方法会一直阻塞,等待_go字段变成true,Monitor.Wait方法按顺序的做了以下的操作。
1:释放锁_locker;
2:阻塞锁,直到_locker
是”pulsed”。
3:重新在_locker
上获取锁,如果锁已经被其他线程获得,那么线程开始阻塞,直到锁变得可用为止。
如果我们抛弃该模式,例如移除while循环。_go字段和ReadLine方法等:
static object _locker = new object();
internal static void Main()
{
new Thread(Work).Start();
lock (_locker) Monitor.Pulse(_locker);
}
static void Work()
{
lock (_locker) Monitor.Wait(_locker);
Console.WriteLine("被唤醒了");
}
那么程序运行的结果又如何呢?
实际上输出是不确定的,有可能你不能显示“被唤醒了”。
主要原因是主线程和Work线程之间存在着竞争关系,如果Wait方法先执行,那么可以正常显示,但是如果Pulse方法先执行,pulse就会丢失,worker线程就会永远的等待。这种行为和AutoResetEvent不同,AutoResetEvent的Set方法有一种记忆的效果,所以即使它在WaitOne方法前调用,它仍然有效。
但是Pulse没有记忆功能,因为你希望自己实现记忆功能,就像我们之前使用_go
标志一样,
这就是为什么Wait和Pulse是通用的原因:使用一个boolean
标志,我们可以实现AutoResetEvent的功能,使用一个integer标志,我们可以实现 CountdownEvent,Semaphore.使用更复杂的
c45e
结构,我么可以写一些更复杂的构造,例如
生产/消费者队列。
相关文章推荐
- LeetCode 172 Factorial Trailing Zeroes
- [LeetCode] Container With Most Water 简要分析
- [LeetCode] Container With Most Water 简要分析
- 【LeetCode】219 Contains Duplicate II
- AC 与 GLPI的MAC地址自动同步脚本 expect mail ver4.0【最新】
- for update 和for update nowait
- SCP报错:Host key verification failed
- 杭电-2674N!Again(大数阶乘)
- poj3757 Training little cats
- 理解Explain命令输出中的filesort
- Exception in thread "main" org.hibernate.SessionException: Session is closed!
- Exception in thread "main" org.hibernate.HibernateException: No CurrentSessionContext configured!
- AEAI DP开发平台精要
- HDOJ 2674-N!Again【数论】
- Contains Duplicate
- UVa 10651 Pebble Solitaire(状压DP)
- fail2ban安装配置
- sleep()和wait()有什么区别
- socket failed:EACCES(Permission denied)
- typearray和obtainStyledAttribute的作用