文档章节

同步之条件变量sync.Cond

秋风醉了
 秋风醉了
发布于 2016/08/07 16:06
字数 820
阅读 379
收藏 0

同步之条件变量sync.Cond sync.Cond 的结构体

type Cond struct {
	// L is held while observing or changing the condition
	L Locker

	sema    syncSema
	waiters uint32 // number of waiters
	checker copyChecker
}

sync.Cond 的方法

//阻塞当前的goroutine
//方法Wait会自动的对与该条件变量关联的那个锁进行解锁,并且使调用方所在的Goroutine被阻塞。
//一旦该方法收到通知,就会试图再次锁定该锁。
//如果锁定成功,它就会唤醒那个被它阻塞的Goroutine。
//否则,该方法会等待下一个通知,那个Goroutine也会继续被阻塞。
func (c *Cond) Wait() {
	c.checker.check()
	if race.Enabled {
		race.Disable()
	}
        //原子递增等待者计数,然后获取信号量进入休眠
	atomic.AddUint32(&c.waiters, 1)
	if race.Enabled {
		race.Enable()
	}
	c.L.Unlock()
	runtime_Syncsemacquire(&c.sema)
	c.L.Lock()
}

func (c *Cond) Signal() {
}

func (c *Cond) Broadcast() {
}

先看一个简单的用法,这样一个场景: 在控制台输入enter,作为一个发送通知的信号,使阻塞的goroutine继续执行。

package main

import (
	"os"
	"fmt"
	"sync"
	"time"
)

func main() {

	locker := sync.Mutex{}
	cond := sync.NewCond(&locker)

	go func() {
		os.Stdin.Read(make([]byte, 1)) // read a single byte
		cond.Signal()//当键盘输入enter后,发出通知信号
		fmt.Println("signal...")
	}()

	go func() {
		cond.L.Lock() //首先进行锁定,与之关联的条件变量的锁定
		fmt.Println("wait before...")
		//等待Cond消息通知
		cond.Wait()
		fmt.Println("wait end...")
		cond.L.Unlock()
	}()

	time.Sleep(10 * time.Second)
	fmt.Println("exit...")
}

运行结果,

wait before...

signal...
wait end...
exit...

在打印出wait before...后,然后在控制台输入空格,最后wait end。 问题来了,如果在wait 之前输入了enter怎么办,也就是说提前发出了信号怎么办?如下代码,

package main

import (
	"os"
	"fmt"
	"sync"
	"time"
)

func main() {
	var wg sync.WaitGroup

	locker := sync.Mutex{}
	cond := sync.NewCond(&locker)

	go func() {
		os.Stdin.Read(make([]byte, 1)) // read a single byte
		cond.Signal()//当键盘输入enter后,发出通知信号
		fmt.Println("signal...")
	}()

	time.Sleep(5 * time.Second)
	fmt.Println("sleep end...")

	wg.Add(1)
	go func() {
		defer wg.Done()
		cond.L.Lock() //首先进行锁定,与之关联的条件变量的锁定
		fmt.Println("wait before...")
		//等待Cond消息通知
		cond.Wait()
		fmt.Println("wait end...")
		cond.L.Unlock()
	}()

	wg.Wait()
	fmt.Println("exit...")
}

运行结果,


signal...
sleep end...
wait before...
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [semacquire]:
sync.runtime_Semacquire(0xc82000a28c)
	/usr/local/go/src/runtime/sema.go:47 +0x26
sync.(*WaitGroup).Wait(0xc82000a280)
	/usr/local/go/src/sync/waitgroup.go:127 +0xb4
main.main()
	/Users/xinxingegeya/gogogo/ucar.com/cond/hello.go:36 +0x245

goroutine 7 [semacquire]:
sync.runtime_Syncsemacquire(0xc820010290)
	/usr/local/go/src/runtime/sema.go:241 +0x201
sync.(*Cond).Wait(0xc820010280)
	/usr/local/go/src/sync/cond.go:63 +0x9b
main.main.func2(0xc82000a280, 0xc820010280)
	/Users/xinxingegeya/gogogo/ucar.com/cond/hello.go:31 +0x159
created by main.main
	/Users/xinxingegeya/gogogo/ucar.com/cond/hello.go:34 +0x237
exit status 2

Process finished with exit code 1

在sleep end...之前输入enter,这时会发出信号,当sleep 结束之后,当执行接下来的goroutine时就会发生错误,即使wait了,也不会发出信号了。怎么才能避免这种情况呢?推荐用法如下,

package main

import (
	"fmt"
	"os"
	"sync"
	"time"
)

func main() {
	var wg sync.WaitGroup

	locker := sync.Mutex{}
	cond := sync.NewCond(&locker)

	var condition bool = false

	go func() {
		os.Stdin.Read(make([]byte, 1)) // read a single byte
		cond.L.Lock()
		cond.Signal()    //当键盘输入enter后,发出通知信号
		condition = true //把条件变量设为true,表示发送过信号
		fmt.Println("signal...")
		cond.L.Unlock()
	}()

	time.Sleep(5 * time.Second)
	fmt.Println("sleep end...")

	wg.Add(1)
	go func() {
		defer wg.Done()
		cond.L.Lock() //首先进行锁定,与之关联的条件变量的锁定
		fmt.Println("wait before...")
		//等待Cond消息通知
		for !condition {
			//当条件为真时,不会发生wait
			fmt.Println("wait...")
			cond.Wait()
		}
		fmt.Println("wait end...")
		cond.L.Unlock()
	}()

	wg.Wait()
	fmt.Println("exit...")
}

在sleep 之前输入enter,


signal...
sleep end...
wait before...
wait end...
exit...

在sleep之后输入enter,

sleep end...
wait before...
wait...

signal...
wait end...
exit...

=======END=======

© 著作权归作者所有

共有 人打赏支持
秋风醉了
粉丝 236
博文 577
码字总数 418437
作品 0
朝阳
程序员
Golang Cond源码分析

cond的主要作用就是获取锁之后,wait()方法会等待一个通知,来进行下一步锁释放等操作,以此控制锁合适释放,释放频率,适用于在并发环境下goroutine的等待和通知。 针对Golang 1.9的sync.Co...

梦朝思夕
04/22
0
0
Golang sync.Cond源码分析

cond的主要作用就是获取锁之后,wait()方法会等待一个通知,来进行下一步锁释放等操作,以此控制锁合适释放,释放频率,适用于在并发环境下goroutine的等待和通知。 针对Golang 1.9的sync.Co...

梦朝思夕
04/23
0
0
使goroutine同步的方法总结

前言: 在前面并发性能对比的文章中,我们可以看到Golang处理大并发的能力十分强劲,而且开发也特别方便,只需要用go关键字即可开启一个新的协程。 但当多个goroutine同时进行处理的时候,就会...

oneHand
01/29
3
0
Java线程:条件变量 lock

条件变量是Java5线程中很重要的一个概念,顾名思义,条件变量就是表示条件的一种变量。但是必须说明,这里的条件是没有实际含义的,仅仅是个标记而已,并且条件的含义往往通过代码来赋予其含...

古月楼
2013/08/26
0
0
Linux——互斥锁与条件变量(二)

三、对比上锁与等待 在上述的生产者-消费者问题中,我们在实现同步的时候还可以用以下的方法来实现,这里只是说明与上述实现中的不同之处。 1、首先,说明一下此版本中的相关特征: 在此版本...

KiteRunner
2014/06/02
0
0

没有更多内容

加载失败,请刷新页面

加载更多

中秋快乐!!!

HiBlock
41分钟前
1
0
Node安装教程

1、安装最新版的node 2、设置相关目录(以D盘为例) 分别建立目录:D:\node,D:\node\node-globa,D:\node\node-cache 命令行输入: // 设置npm国内镜像 npm config set registry https://re...

Mohan710
今天
3
0
中国发布域名系统基础软件 “红枫”

9月12日消息,域名工程中心(英文缩写 ZDNS)发布了宣称自主开发的域名系统基础软件 “红枫(Maple DNS)”。 9月12日消息,域名工程中心(英文缩写 ZDNS)发布了宣称自主开发的域名系统基础软...

问题终结者
今天
3
0
Shell编程(分发系统介绍、expect远程登录、expect远程执行命令、expect传递参数)

分发系统介绍expect 分发系统expect即分发脚本,是一种脚本语言;通过他可以实现传输,输入命令(上线代码) 应用场景:业务越来越大,网站app,后端,编程语言是php,所以就需要配置lamp或者...

蛋黄_Yolks
今天
4
0
Java Http请求工具类

public static String httpPost(String source, String params) {URL url = null;HttpURLConnection conn = null;OutputStream os = null;String ret = null;try {......

yuewawa
今天
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部