channel

死锁


package main

import "fmt"

func callerA(c chan string) {
	c <- "Hello world! callerA"
}

func callerB(c chan string) {
	c <- "Hello world! callerB"
}

func main() {
	a, b := make(chan string), make(chan string)
	go callerA(a)
	go callerB(b)
	for i := 0; i < 5; i++ {
		select {
		case msg := <-a:
			fmt.Printf("%v from A\n", msg)
		case msg := <-b:
			fmt.Printf("%v from B\n", msg)
		}
	}
}

/*
fatal error: all goroutines are asleep - deadlock!
Hello worldBBBBBBBB! from B
Hello world! from A

goroutine 1 [select]:
main.main()
	C:/Users/zhourudong/go/src/awesomeProject/main.go:18 +0x19c
*/

/*
死锁原因:
	当第一次循环时,main从a通道取值后,a通道为空;

	当第二次循环时,main从b通道取值后,b通道为空;

	当第三次循环时,main函数发现a/b通道都为空,callerA、callerB两个goroutine都已执行完毕,
	a,b通道永远不会有值,main函数无止境的等待下去,go运行环境会抛出死锁错误

*/


死锁解决

package main

import "fmt"

func callerA(c chan string) {
	c <- "Hello world! callerA"
}

func callerB(c chan string) {
	c <- "Hello world! callerB"
}

func main() {
	a, b := make(chan string), make(chan string)
	go callerA(a)
	go callerB(b)
	for i := 0; i < 5; i++ {
		select {
		case msg := <-a:
			fmt.Printf("%v from A\n", msg)
		case msg := <-b:
			fmt.Printf("%v from B\n", msg)
		default:
			fmt.Println("Default")
		}
	}
}

/*
Default
Hello world! callerB from B
Hello world! callerA from A
Default
Default
*/

/*
输出 Default的原因是因为 
通道还没有传入值,初始化完成,所以第一个只能输出Default

解决方案:
	休眠
*/


// ---------------------------------------------------
package main

import (
	"fmt"
	"time"
)

func callerA(c chan string) {
	c <- "Hello world! callerA"
}

func callerB(c chan string) {
	c <- "Hello world! callerB"
}

func main() {
	a, b := make(chan string), make(chan string)
	go callerA(a)
	go callerB(b)
	for i := 0; i < 5; i++ {
		time.Sleep(3 * time.Microsecond)
		select {
		case msg := <-a:
			fmt.Printf("%v from A\n", msg)
		case msg := <-b:
			fmt.Printf("%v from B\n", msg)
		default:
			fmt.Println("Default")
		}
	}
}

/*
Hello world! callerA from A
Hello world! callerB from B
Default
Default
Default
*/

不使用sleep解决方案


/*
1、尝试向一个已关闭的通道发送信息会引发panic
2、尝试关闭一个已关闭的通道也会触发panic
3、尝试从一个已关闭的通道取值总得到一个与通道类型相对应的零值,
	所以从已关闭的通道取值不会导致goroutine被阻塞
*/
package main

import "fmt"

func callerA(c chan string) {
	// 函数被调用后关闭
	c <- "Hello world callerA"
	close(c)
}
func callerB(c chan string) {
	// 函数被调用后关闭
	c <- "Hello world callerB"
	close(c)
}

func main() {
	a, b := make(chan string), make(chan string)
	go callerA(a)
	go callerB(b)

	var msg string
	ok1, ok2 := true, true

	for ok1 || ok2 {
		select {
		case msg, ok1 = <-a: //  通道取出的值会赋值给msg,ok1则表示通道已经关闭则被设置成false
			if ok1 {
				fmt.Printf("%s from A\n", msg)
			}
		case msg, ok2 = <-b:
			if ok2 {
				fmt.Printf("%s from B\n", msg)
			}

		}
	}
}

/*
1、尝试向一个已关闭的通道发送信息会引发panic
2、尝试关闭一个已关闭的通道也会触发panic
3、尝试从一个已关闭的通道取值总得到一个与通道类型相对应的零值,
	所以从已关闭的通道取值不会导致goroutine被阻塞
*/
package main

import (
	"fmt"
)

func callerA(c chan int) {
	// 函数被调用后关闭
	c <- 1111
	close(c)
}

func main() {
	a := make(chan int)
	go callerA(a)

	var ok bool
	var msg int

	msg, ok = <-a // 第一次通道还没有关闭 返回值和一个布尔值,

	fmt.Println(msg, ok)

	msg, ok = <-a // 通道已关闭, 得到与通道相对应类型的零值,一个为false的布尔值
	fmt.Println(msg, ok)

}

/*
1111 true
0 false
*/

posted @ 2018-03-24 11:12  cucy_to  阅读(169)  评论(0)    收藏  举报