一、leetcode 1114
import (
"fmt"
)
func printFirst(notifySecond chan int) {
fmt.Println("first")
close(notifySecond)
}
func printSecond(notifySecond chan int, notifyThird chan int) {
select {
case <-notifySecond:
fmt.Println("second")
}
close(notifyThird)
}
func printThird(notifyThird chan int) {
select {
case <-notifyThird:
fmt.Println("third")
}
}
func main() {
ch1 := make(chan int)
ch2 := make(chan int)
go printThird(ch2)
go printFirst(ch1)
//go printThird(ch2)
go printSecond(ch1, ch2)
for {
}
}
我们通过两个channel来互相通知,要求必须执行 打印first-通知打印second-打印second-通知打印third。
二、交替打印foo bar
func printFoo(myCh, otherCh chan int) {
for {
select {
case <-myCh:
fmt.Println("foo")
time.Sleep(1 * time.Second)
otherCh <- 1
}
}
}
func printBar(myCh, otherCh chan int) {
for {
select {
case <-myCh:
fmt.Println("bar")
time.Sleep(1 * time.Second)
otherCh <- 1
}
}
}
func main() {
ch1 := make(chan int)
ch2 := make(chan int)
go printFoo(ch1, ch2)
go printBar(ch2, ch1)
ch1 <- 1//start all
for {
}
}
我们思考为了让他们交替打印,需要两个goroutinue之间有双向通信的装置,ch1代表发送给foo函数,触发它的打印;ch2代表发送给bar函数,触发它的打印。打印结束后,需要向另一个channel发送消息,通知另一个goroutinue,这就成了一个轮流通知对方的过程。不过需要注意的是,刚开始channel里面没有任何数据,需要手动触发它的第一次打印(这里如果先发给ch1那就是先打印foo)
三、打印零和奇偶数
分析一下其实并不难,简单来说每次通知到底是odd协程打印还是even协程打印由zero协程控制,但是odd和even协程一旦执行完毕必须通知zero。
ps:这里其实就体现出线程间协同并发的基本原理就是“通知”,无论要求做出什么样的协同场景,都要先分析线程间的通知流程。
var globalCnt int = 0
var maxN int = 0
var zeroChan = make(chan int)
var oddChan = make(chan int)
var evenChan = make(chan int)
func printNumber(n int) {
fmt.Printf("%d", n)
}
func zero() {
for {
select {
case <-zeroChan:
if globalCnt >= maxN {
return
}
printNumber(0)
time.Sleep(1 * time.Second)
if globalCnt%2 == 1 {
oddChan <- 1
} else {
evenChan <- 1
}
}
}
}
func odd() {
for {
select {
case <-oddChan:
globalCnt++
printNumber(globalCnt)
time.Sleep(1 * time.Second)
zeroChan <- 1
}
}
}
func even() {
for {
select {
case <-evenChan:
globalCnt++
printNumber(globalCnt)
time.Sleep(1 * time.Second)
zeroChan <- 1
}
}
}
func main() {
maxN = 3
go zero()
go even()
go odd()
zeroChan <- 1
for {
}
}
四、H2O生成
这个题要求使用两种线程要求生产的一定是一组[H,H,O]字符,为了验证最终正确性,我们将随机启动m个H线程(协程)和n个O线程(协程),打印应当是3的倍数而且每组都是2个H一个O。
分析通知流程可知,O协程应当一次性通知2个H协程,而且H协程对O协程的通知,应当收到2个信号才能符合要求。
var hCh = make(chan int, 2)
var oCh = make(chan int, 2)
var cnt = &Counter{0}
type Counter struct {
cnt int32
}
func (c *Counter) Increase() {
atomic.AddInt32(&c.cnt, 1)
}
func (c *Counter) GetValue() int32 {
return c.cnt
}
func hydrogen() {
select {
case <-hCh:
fmt.Printf("H")
oCh <- 1
}
}
func oxygen() {
select {
case <-oCh:
if cnt.GetValue()%2 == 0 {
fmt.Printf("O")
hCh <- 1
hCh <- 2
}
cnt.Increase()
}
}
func init() {
rand.Seed(time.Now().UnixNano())
}
func main() {
for i := 0; i < 30; i++ {
t := rand.Intn(197) % 3
if t == 0 {
//fmt.Println("start oxygen")
go oxygen()
} else {
//fmt.Println("start hydrogen")
go hydrogen()
}
}
oCh <- 1
for {
}
}