1、条件变量模式
有时线程需要根据某条件为true时,才去执行相关的操作。一般的方式可能是
while(someCondiction){
//相关的操作
}
写一个while循环,一直在判断条件是否为true。但这样缺点太明显,占用CPU。阻塞了其他线程。
幸好,Monitor有一个Wait、Pulse方法。具体方法如下:
public static class Monitor{
public static bool Wait(object obj);
public static bool Wait(object obj,int millisecondsTimeout);
public static void Pulse(object obj);
public static void PulseAll(object obj)
}
Wait方法的作用是临时释放锁,Pulse方法则是唤醒阻塞的线程。具体的例子如下:
internal sealed class ConditionVariablePattern{
//私有锁
private readonly object m_lock = new object();
//m_condition也可以为一个复杂的条件变量
private bool m_condition=false;
//线程1执行的相关内容
public void Thread1(){
Monitor.Enter(m_lock);//获得锁
while(!m_condition){
//Wait方法可以使线程临时释放锁,这样其他的线程就可以获得锁,以便执行相关操作
Monitor.Wait(m_lock);
}
//能够执行到这儿,说明条件已经满足
//因此这儿可以写具体的业务逻辑
Monitor.Exit(m_lock); //等到最后一定要彻底释放锁
}
public void Thread2(){
//先是获取锁
Monitor.Enter(m_lock);
//
m_condition=true;
//唤醒一个等待时间最长的线程
Monitor.Pulse(m_lock);
//Monitor.PulseAll(m_lock); //代表唤醒所有等待的线程
//当线程调用Exit方法时,被唤醒的线程就会拥有锁。
//若使用了PulseAll唤醒了所有的线程,则当调用Exit方法时,本线程锁来源的线程将会获得锁
Monitor.Exit(m_lock);
}
}
此处还有另外一个例子:在一个队列中,当进行入队、出队时,要保证数据同步。例子如下:
internal sealed class SynchronizedQueue<T>{
private readonly object m_lock = new object();
private readonly Queue<T> m_queue = new Queue<T>();
//入队
public void Enqueue(T item){
Monitor.Enter(m_lock);
m_queue.Enqueue(item);
Monitor.PulseAll(m_lock); //若不加这句,则需要等CLR自己根据时间去唤醒阻塞线程
Monitor.Exit(m_lock);
}
//出队
public T Dequeue(){
Monitor.Enter(m_lock);
//判断一般都会加上,但Wait很少加上
//但是加了Wait之后会立刻释放锁,让其他阻塞的线程获得了锁,提升了性能
if(m_queue.Count==0) Monitor.Wait(m_lock);
T item=m_queue.Dequeue();
Monitor.Exit(m_lock);
return item;
}
以上就是条件变量模式
多线程总结
从去年的11月份至3月初。历时4个月才看完《CLR via C#》的第四部分--多线程。回顾下整个多线程,其根本的目的就是更加“有效”的使用CPU,因此针对不同的情况有不同的类、方法。由Thread到Task,再到异步,再到同步,都是如此。在实际的工作中,用到最多的就是Task,其他的异步、同步等功能暂未用到。
下一步计划:从3月份至5月份,完成《CLR via C#》第三部分的阅读及笔记。