同步之条件变量sync.Cond

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

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 的方法atom

//阻塞当前的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继续执行。code

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怎么办,也就是说提早发出了信号怎么办?以下代码,it

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...")
}

运行结果,io

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了,也不会发出信号了。怎么才能避免这种状况呢?推荐用法以下,ast

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,import

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

在sleep以后输入enter,变量

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

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

=======END=======方法

相关文章
相关标签/搜索