分享好友 编程语言首页 频道列表

深入多线程之:Wait与Pulse的使用详解

C#教程  2015-11-04 11:020

Signaling with Wait and Pulse(等待和暂停的信号)

早期谈论过等待事件句柄(调用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之前会一直等待。

复制代码 代码如下:

static readonly object _locker = new object();
        static bool _go;

        internal static void Main()
        {
            new Thread(Work).Start(); //新线程会被阻塞,因为_go == false
            Console.ReadLine(); //等待用户输入

            lock (_locker)
            {
                _go = true; //改变阻塞条件
                Monitor.Pulse(_locker); //通知等待的队列。
            }
        }

        static void Work()
        {
            lock (_locker)
            {
                while (!_go) //只要_go字段是false,就等待。
                    Monitor.Wait(_locker); //在等待的时候,锁已经被释放了。
            }

            Console.WriteLine("被唤醒了");
        }


为了线程安全,确保所有共享的字段在读取的时候都加锁了。

Work方法会一直阻塞,等待_go字段变成true,Monitor.Wait方法按顺序的做了以下的操作。

1:释放锁_locker;

2:阻塞锁,直到_locker 是”pulsed”。

3:重新在_locker 上获取锁,如果锁已经被其他线程获得,那么线程开始阻塞,直到锁变得可用为止。

复制代码 代码如下:

lock(_locker)
{
    While(!_go)
        Monitor.Wait(_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.使用更复杂的结构,我么可以写一些更复杂的构造。

查看更多关于【C#教程】的文章

展开全文
相关推荐
反对 0
举报 0
评论 0
图文资讯
热门推荐
优选好物
更多热点专题
更多推荐文章
[微信小程序] 终于可以愉快的使用 async/await 啦
这篇文章主要是想说一下 怎么在微信小程序中使用async/await从而逃离回调地狱背景最近一直在搞微信小程序 用的语言是TypeScript 小程序的api都是回调形式 用起来就是各种回调嵌套 我个人很不喜欢 所以一直想用async/await之前用TypeScript target到ES2015 可

0评论2023-02-09918

【dart学习】-- Dart之async和await
一,概述在Dart1.9中加入了async和await关键字,有了这两个关键字,我们可以更简洁的编写异步代码,而不需要调用Future相关的API。他们允许你像写同步代码一样写异步代码和不需要使用Future接口。相当于都Future相关API接口的另一种封装,提供了一种更加简便

0评论2023-02-09896

关于nodejs中的async/await nodejs详解
作用:将异步转为同步,其实有点语法糖,promise能实现的改为比较同步的方式表现。用法:两个关键字:async:放在函数声明前,用于表示这个函数含有异步过程,且此函数必定返回promise对象await:只能用于async声明了的函数里,如果await的对象是个promise,

0评论2023-02-09912

Nodejs中async/await以及Promise的使用
场景:上传图片注意:try/catch是不能catch住Promise异步异常的其它:Nest.js中上传图片和裁剪async uploadAction (req, res) {try { // 同步调用 await saveFileWithStream(filePath, fileData); // 这里的fileData是Buffer类型} catch (err) { console.lo

0评论2023-02-09431

Go语言通过WaitGroup实现控制并发的示例详解
目录与Channel区别基本使用示例完整代码特别提示多任务示例完整代码与Channel区别Channel能够很好的帮助我们控制并发,但是在开发习惯上与显示的表达不太相同,所以在Go语言中可以利用sync包中的WaitGroup实现并发控制,更加直观。基本使用示例我们将之前的示

0评论2023-02-09745

Rust 异步编程,async await
   https://learnku.com/docs/async-book/2018/http_server_example/4789   //例子二use futures::{ self, executor};async fn learn_song() {println!("Learn song!");}async fn sing_song() {println!("Sing song!");}async fn dance() {println!("D

0评论2023-02-09452

Rust 实现 async/await的详细代码
目录FutureWakeContext为什么需要 executor ?什么是 waker ?async/awaitExecutorWaker struct 到 ArcWake traitFuturesUnordered单线程 executor线程池 executor总结异步编程在 Rust 中的地位非常高,很多 crate 尤其是多IO操作的都使用了 async/await.首先

0评论2023-02-09968

Delphi中线程类TThread实现多线程编程2---事件、临界区、Synchronize、WaitFor……
  接着上文介绍TThread。  现在开始说明 Synchronize和WaitFor  但是在介绍这两个函数之前,需要先介绍另外两个线程同步技术:事件和临界区事件(Event)  事件(Event)与Delphi中的事件有所不同。从本质上讲,Event其实就相当于一个全局的布尔变量

0评论2023-02-09807

Dart(八)Future、async、await异步
转 https://www.jianshu.com/p/304f05a442db同步方法Dart通常是单线程执行:如:String method1() {return "1";}String method2() {return "2";}String method3() {return "3";}void testA() {print(method1());print(method2());print(method3());}则输出:

0评论2023-02-08915

Go并发控制之sync.WaitGroup golang 并发控制
    WaitGroup 会将main goroutine阻塞直到所有的goroutine运行结束,从而达到并发控制的目的。使用方法非常简单,真心佩服创造Golang的大师们!WaitGroup               //相当于一个箱子,将main goroutine 保护到里面Add   //调用一次为箱子

0评论2023-02-08711

Go sync WaitGroup使用深入理解
目录基本介绍使用源码分析AddDoneWait注意事项基本介绍WaitGroup是go用来做任务编排的一个并发原语,它要解决的就是并发 - 等待的问题:当有一个 goroutine A 在检查点(checkpoint)等待一组 goroutine 全部完成,如果这些 goroutine 还没全部完成,goroutin

0评论2023-02-08887

Nodejs新特性async和await的使用详解
目录1.Es6常见语法的使用2.Async、Await和Promise1.Es6常见语法的使用1.let、constlet:是一个块作用域if (true) {let a = 123;}console.log(a);// a is not definedconst:定义常量const PI = 3.1415926;PI = 3.15// Assignment to constant variable.consol

0评论2023-02-07824

Golang中的sync.WaitGroup用法实例
这篇文章主要介绍了Golang中的sync.WaitGroup用法实例,WaitGroup的用途,它能够一直等到所有的goroutine执行完成,并且阻塞主线程的执行,直到所有的goroutine执行完成,需要的朋友可以参考下

0评论2015-11-06114

更多推荐