63-条件变量分析

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_33781658/article/details/84503111


我们先来这么一段代码


func main() {

	channel := make(chan int)

	for i := 0; i < 5; i++ {
		produce(channel, i)
	}

	for i := 0; i < 5; i++ {
		consume(channel, i)
	}

	select {}
}

func produce(channel chan int, i int) {
	channel <- i
	fmt.Println("生产了", i)
}

func consume(channel chan int, i int) {
	<-channel
	fmt.Println("消费了", i)
}

这里的运行结果是
fatal error: all goroutines are asleep - deadlock!
也就是死锁了

为什么呢 我们来分析一下
在第一次进入produce的时候
直接就使用channel通道发送了一个i
而此时,根本没有其他的go程来进行接收
而且下面的接收也是在主线程
那么肯定会锁死





现在我们把代码修改一下

func main() {

	channel := make(chan int)

	for i := 0; i < 10; i++ {
		go produce(channel, i)
	}

	for i := 0; i < 10; i++ {
		go consume(channel, i)
	}

	select {}
}

func produce(channel chan int, i int) {
	channel <- i
	fmt.Println("生产了", i)
}

func consume(channel chan int, i int) {
	<-channel
	fmt.Println("------消费了", i)
}


我们看一下运行结果  这里我只放一部分

------消费了 9
fatal error: all goroutines are asleep - deadlock!
生产了 1
生产了 7
生产了 0
------消费了 2
生产了 2

1.首先,生产顺序和消费顺序分别是乱序的,这是由于我们几乎同时
开启了10个生产和10个消费,很快就完成了传输和接收,
然后fmt.println这个方法其实是很慢的,所以是乱序的
2.生产和消费是乱序的,不是生产1个,消费1个,
这个其实和第一个是一个道理,这20个go程几乎是同时的
那么很快完成了传输和接收,生产和消费也就乱序了
3.出现了死锁,我们可以看一下
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [select (no cases)]:
main.main()
已经说的很清楚了,主线程不能出现没有case的select
所以改成for循环吧,
go程里面写没有case的select是没关系的



那么 就改成for循环

func main() {

	channel := make(chan int)

	for i := 0; i < 10; i++ {
		go produce(channel, i)
	}

	for i := 0; i < 10; i++ {
		go consume(channel, i)
	}

	for{
		;
	}
}

func produce(channel chan int, i int) {
	channel <- i
	fmt.Println("生产了", i)
}

func consume(channel chan int, i int) {
	<-channel
	fmt.Println("------消费了", i)
}

现在没有报错了,但是还是乱序
我们看下结果
------消费了 0
------消费了 3
------消费了 2
------消费了 6
------消费了 1
------消费了 7
生产了 2






现在,我们加入sync.Mutex

var lock sync.Mutex

func main() {

	channel := make(chan int)

	for i := 0; i < 10; i++ {
		go produce(channel, i)
	}

	for i := 0; i < 10; i++ {
		go consume(channel, i)
	}

	for  {
		;
	}
}

func produce(channel chan int, i int) {
	lock.Lock()
	channel <- i
	fmt.Println("生产了", i)
	lock.Unlock()
}

func consume(channel chan int, i int) {
	lock.Lock()
	<-channel
	fmt.Println("------消费了", i)
	lock.Unlock()
}

运行结果: 什么都没有

现在我们来调查一下
我试了一下,不管是用两个lock还是1个lock
好像都不行

现在我们来试一下条件变量

然后就变成了这样


var cond sync.Cond

func producer(in chan<- int) {
	for {
		cond.L.Lock()
		for len(in) == 3 {
			cond.Signal()
			cond.Wait()
		}
		num := rand.Intn(100)
		fmt.Println("生产了", num)
		in <- num

		cond.L.Unlock()
	}
}

func consumer(out <-chan int) {
	for {
		cond.L.Lock()
		for len(out) == 0 {
			cond.Signal()
			cond.Wait()
		}
		num := <-out
		fmt.Println("消费了:", num)

		cond.L.Unlock()
	}
}

func main() {
	rand.Seed(time.Now().UnixNano())
	cond.L = new(sync.Mutex)
	c := make(chan int, 3)
	for i := 1; i <= 5; i++ {
		go producer(c)
	}
	for i := 1; i <= 5; i++ {
		go consumer(c)
	}
	select {}
}




我们先从简单的开始

var cond sync.Cond

func main() {
	cond.L = new(sync.Mutex)

	channel := make(chan int, 3)

	for i := 0; i < 5; i++ {
		go produce(channel)
	}

	for i := 0; i < 5; i++ {
		go consume(channel)
	}

	select {}
}

func produce(channel chan int, ) {
	for i := 0; i < 100; i++ {
		channel <- i
		fmt.Println("生产了", i)
	}
}

func consume(channel chan int) {
	for i := 0; i < 100; i++ {
		<-channel
		fmt.Println("......消费了", i)
	}
}

现在我们创建了5个go程,负责生产,5个go程负责消费






然后我们加上锁试试吧

var cond sync.Cond

func main() {
	cond.L = new(sync.Mutex)

	channel := make(chan int, 3)

	for i := 0; i < 5; i++ {
		go produce(channel)
	}

	for i := 0; i < 5; i++ {
		go consume(channel)
	}

	select {}
}

func produce(channel chan int, ) {
	for i := 0; i < 100; i++ {
		cond.L.Lock()

		if len(channel) == 3 {
			cond.Broadcast()
			cond.Wait()
		}

		channel <- i
		fmt.Println("生产了", i)
		cond.L.Unlock()
	}
}

func consume(channel chan int) {
	for i := 0; i < 100; i++ {
		cond.L.Lock()

		if len(channel) == 0 {
			cond.Broadcast()
			cond.Wait()
		}

		<-channel
		fmt.Println("......消费了", i)
		cond.L.Unlock()
	}
}


还是死锁了
到底是怎么回事




我们再来一份简单代码

var cond sync.Cond

func main() {

	channel := make(chan int, 3)

	for i := 0; i < 5; i++ {
		go produce(channel)
	}

	for i := 0; i < 5; i++ {
		go consume(channel)
	}

	for {
		;
	}
}

func produce(channel chan int) {
	for i := 0; i < 50; i++ {
		channel <- i
		fmt.Println("生产了", i)
	}
}

func consume(channel chan int) {
	for i := 0; i < 50; i++ {
		num := <-channel
		fmt.Println("......消费了", num)
	}
}


然后我们加上锁


var cond sync.Cond

func main() {
	cond.L = new(sync.Mutex)
	channel := make(chan int, 3)

	for i := 0; i < 5; i++ {
		go produce(channel)
	}

	for i := 0; i < 5; i++ {
		go consume(channel)
	}

	for {
		;
	}
}

func produce(channel chan<- int) {
	for i := 0; i < 50; i++ {
		cond.L.Lock()
		if len(channel) == 3 {
			cond.Broadcast()
			cond.Wait()
		}
		channel <- i
		fmt.Println("生产了", i)
		cond.L.Unlock()
	}
}

func consume(channel <-chan int) {
	for i := 0; i < 50; i++ {
		cond.L.Lock()
		if len(channel) == 0 {
			cond.Broadcast()
			cond.Wait()
		}
		num := <-channel
		fmt.Println("......消费了", num)
		cond.L.Unlock()
	}
}


好了  搞定
生产了 15
生产了 16
生产了 17
......消费了 15
......消费了 16
......消费了 17
搞定了






猜你喜欢

转载自blog.csdn.net/qq_33781658/article/details/84503111
今日推荐