深入多线程之:用Wait与Pulse模拟一些同步构造的应用详解

你可能在上篇文章中《深入多线程之:双向信号与竞赛的用法分析》注意到了这个模式:两个Waiting 循环都要下面的构造:


代码如下:

lock(_locker)
{
        while(!_flag) Monitor.Wait(_locker);
        _flag = false;
}

在这里_flag被另一线程设置为true。这是,从作用上讲,这里在模仿AutoResetEvent。如果我们将 _flag = false;去掉,那么我们就得到了一个基本的ManualResetEvent.

让我们使用Wait和Pulse来为ManualResetEvent完成剩余的代码吧。


代码如下:

readonly object _locker = new object();
        bool _signal;

void WaitOne()
        {
            lock (_locker)
            {
                while (!_signal) Monitor.Wait(_locker);
            }
        }

void Set()
        {
            lock (_locker) { _signal = true; Monitor.PulseAll(_locker); }
        }

void Reset() { lock (_locker) _signal = false; }

在这里使用PulseAll,是因为可能有很多阻塞的线程。

如果在WaitOne方法中增加_signal=false就可以简单的模拟AutoResetEvent.例如:


代码如下:

void WaitOne()
        {
            lock (_locker)
            {
                while (!_signal) Monitor.Wait(_locker);
                _signal = false; //实现自动关闭功|能
            }
        }

然后在Set方法中,将PulseAll修改为Pulse

Lock(_locker) {_signal = true; Monitor.Pulse(_locker);}

如果使用的是int类型的_signal 标志,那么我们可以得到一个最基本的Semaphore.

Waiting Queues and PulseAll

当多余一个线程在同一个对象上面等待的时候,一个 “等待队列(waiting queue)” 就形成了。

每一次调用Pulse都会释放在”等待队列”头部的一个线程。下面的图形象的展示了这一点:

线程调用Monitor.Enter 进入ReadyQueue. 等待获取锁,成功获取锁后,如果正常的执行,那么之后会调用Monitor.Exit退出,

否则如果获取了锁之后发现需要等待其他的线程或者是其他阻塞条件,那么调用Wait方法,就进入了等待队列,

当等待的线程完成并调用Pulse后,处在WaitingQueue头部的线程就被 Pulse了,等待CPU调度 。之后再次进入Ready Queue,重新获取锁。

Countdown

借助Wait和Pulse,我们可以实现CountdownEvent的主要功能。例如:


代码如下:

class Countdown
    {
        object _locker = new object();
        int _value; //使用_value来计数

public Countdown() { }
        public Countdown(int initialCount) { _value = initialCount; }

public void Singnal() { AddCount(-1); } //将计数减一

public void AddCount(int amount)
        {
            lock (_locker)
            {
                _value += amount; //将计数增加或减少
                if (_value <= 0) Monitor.PulseAll(_locker);//如果value<=0,说明所有等待的任务都完成了。
            }
        }

public void Wait()
        {
            lock (_locker)
            {
                //只要计数 > 0 就等待。
                while (_value > 0)
                {
                    Monitor.Wait(_locker);
                }
            }
        }
}

这和我们上次的代码几乎一致,只是这次我们的阻塞条件基于一个整型_value标志。

(0)

相关推荐

  • 深入多线程之:用Wait与Pulse模拟一些同步构造的应用详解

    你可能在上篇文章中<深入多线程之:双向信号与竞赛的用法分析>注意到了这个模式:两个Waiting 循环都要下面的构造: 复制代码 代码如下: lock(_locker){        while(!_flag) Monitor.Wait(_locker);        _flag = false;} 在这里_flag被另一线程设置为true.这是,从作用上讲,这里在模仿AutoResetEvent.如果我们将 _flag = false;去掉,那么我们就得到了一个基本的ManualRese

  • java多线程之CyclicBarrier的使用方法

    java多线程之CyclicBarrier的使用方法 public class CyclicBarrierTest { public static void main(String[] args) { ExecutorService service = Executors.newCachedThreadPool(); final CyclicBarrier cb = new CyclicBarrier(3); for(int i=0;i<3;i++){ Runnable runnable = n

  • Java多线程之readwritelock读写分离的实现代码

    在多线程开发中,经常会出现一种情况,我们希望读写分离.就是对于读取这个动作来说,可以同时有多个线程同时去读取这个资源,但是对于写这个动作来说,只能同时有一个线程来操作,而且同时,当有一个写线程在操作这个资源的时候,其他的读线程是不能来操作这个资源的,这样就极大的发挥了多线程的特点,能很好的将多线程的能力发挥出来. 在Java中,ReadWriteLock这个接口就为我们实现了这个需求,通过他的实现类ReentrantReadWriteLock我们可以很简单的来实现刚才的效果,下面我们使用一个例子

  • C#多线程之Thread中Thread.Join()函数用法分析

    本文实例讲述了C#多线程之Thread中Thread.Join()函数用法.分享给大家供大家参考.具体分析如下: Thread.Join()在MSDN中的解释:Blocks the calling thread until a thread terminates 当NewThread调用Join方法的时候,MainThread就被停止执行, 直到NewThread线程执行完毕. Thread oThread = new Thread(new ThreadStart(oAlpha.Beta));

  • Java线程之join_动力节点Java学院整理

    join()介绍 join() 定义在Thread.java中. join() 的作用:让"主线程"等待"子线程"结束之后才能继续运行.这句话可能有点晦涩,我们还是通过例子去理解: // 主线程 public class Father extends Thread { public void run() { Son s = new Son(); s.start(); s.join(); ... } } // 子线程 public class Son extends

  • C#多线程之Thread中Thread.IsAlive属性用法分析

    本文实例讲述了C#多线程之Thread中Thread.IsAlive属性用法.分享给大家供大家参考.具体如下: Thread.IsAlive属性 ,表示该线程当前是否为可用状态 如果线程已经启动,并且当前没有任何异常的话,则是true,否则为false Start()后,线程不一定能马上启动起来,也许CPU正在忙其他的事情,但迟早是会启动起来的! Thread oThread = new Thread(new ThreadStart(Back.Start)); oThread.Start();

  • Java多线程之volatile关键字及内存屏障实例解析

    前面一篇文章在介绍Java内存模型的三大特性(原子性.可见性.有序性)时,在可见性和有序性中都提到了volatile关键字,那这篇文章就来介绍volatile关键字的内存语义以及实现其特性的内存屏障. volatile是JVM提供的一种最轻量级的同步机制,因为Java内存模型为volatile定义特殊的访问规则,使其可以实现Java内存模型中的两大特性:可见性和有序性.正因为volatile关键字具有这两大特性,所以我们可以使用volatile关键字解决多线程中的某些同步问题. volatile

  • java多线程之Phaser的使用详解

    前面的文章中我们讲到了CyclicBarrier.CountDownLatch的使用,这里再回顾一下CountDownLatch主要用在一个线程等待多个线程执行完毕的情况,而CyclicBarrier用在多个线程互相等待执行完毕的情况. Phaser是java 7 引入的新的并发API.他引入了新的Phaser的概念,我们可以将其看成一个一个的阶段,每个阶段都有需要执行的线程任务,任务执行完毕就进入下一个阶段.所以Phaser特别适合使用在重复执行或者重用的情况. 基本使用 在CyclicBar

  • linux创建线程之pthread_create的具体使用

    pthread_create函数 函数简介 pthread_create是UNIX环境创建线程函数 头文件 #include<pthread.h> 函数声明 int pthread_create(pthread_t *restrict tidp,const pthread_attr_t *restrict_attr,void*(*start_rtn)(void*),void *restrict arg); 返回值 若成功则返回0,否则返回出错编号 参数 第一个参数为指向线程标识符的指针. 第二

  • java多线程之Future和FutureTask使用实例

    Executor框架使用Runnable 作为其基本的任务表示形式.Runnable是一种有局限性的抽象,然后可以写入日志,或者共享的数据结构,但是他不能返回一个值. 许多任务实际上都是存在延迟计算的:执行数据库查询,从网络上获取资源,或者某个复杂耗时的计算.对于这种任务,Callable是一个更好的抽象,他能返回一个值,并可能抛出一个异常.Future表示一个任务的周期,并提供了相应的方法来判断是否已经完成或者取消,以及获取任务的结果和取消任务. public interface Callab

随机推荐