Golang两个协程交替输出

jlvihv · 2021-06-03 16:49:34 · 2483 次点击 · 预计阅读时间 2 分钟 · 大约8小时之前 开始浏览    
这是一个创建于 2021-06-03 16:49:34 的文章,其中的信息可能已经有所发展或是发生改变。

题目:

现在有两个goroutine。

一个输出1、3、5、7、9……

另一个输出2、4、6、8、10……

写一段代码,让他们输出1、2、3、4、5、6、7、8、9、10……

解法:

使用Go的channel来解决比较合适。

需要3个channel。

A通道用来记录A协程的状态。

B通道用来记录B协程的状态。

Exit通道用来阻塞主协程,使程序不要立即退出,而是等待我们发出退出信号以后才退出。

Go的channel具有阻塞特性,无缓冲通道中只能存放一个数据。

通道最初是空的,如果想从空通道中读取一个数据,程序就会阻塞,直到向通道中写入一个数据。

如果通道中有一个数据,却没有人来读,程序也将阻塞,直到有人将这个数据读走。

利用这个特性,我们在主goroutine中读取Exit通道,因为我们还没有向Exit通道中写数据,主goroutine将在此阻塞。

主goroutine虽然阻塞了,其他goroutine却是可以正常运行的。

我们设计,其他goroutine把自己的工作都做完之后,向Exit通道写入一个数据。

主goroutine得到这个数据之后,程序不再阻塞,就可以正常退出了。

这样我们就实现了最基本的,有多个goroutine时,工作没做完时等待,都做完后退出的功能了。

然后我们来设计AB两个通道。

利用无缓冲通道阻塞的特性,我们设计:

A协程输出一个数后,向B通道写入一个数据。

B协程输出一个数后,也向A通道写入一个数据。

AB都会时刻检测自己的通道中有没有数据,但是就像接力棒一样,只有另一个协程可以向当前协程的通道传递数据。

而且传递完之后必须等待。

这就保证了,A干活的时候,B必须等着,不能跟A抢活干。

A干完之后,权利交给B,B干完之后,权利再交给A,直到俩人都把活干完。

这就是AB协程交替输出的中间过程,我们还需要设计开始和结束。

开始时,我们在主goroutine中,手动向A协程发送一个开始的信号。

结束时,最后一个把活干完的人(B),向Exit通道中发送一个数据,使主goroutine退出。

实现代码:

package main

import (
  "fmt"
)

func main() {
  // 创建3个channel,A,B和Exit
  A := make(chan bool)
  B := make(chan bool)
  Exit := make(chan bool)

  go func() {
    // 如果A通道是true,我就执行
    for i := 1; i <= 10; i += 2 {
      if ok := <-A; ok {
        fmt.Println("A 输出", i)
        B <- true
      }
    }
  }()

  go func() {
    defer func() { Exit <- true }() // 这个协程的活干完之后,向主goroutine发送信号
    // 如果B通道是true,我就执行
    for i := 2; i <= 10; i += 2 {
      if ok := <-B; ok {
        fmt.Println("B 输出", i)
        if i != 10 { // r如果i等于10了,就不要再向A通道写数据了,否则将导致A通道死锁,至于为什么,坦白说我很疑惑
          A <- true
        }
      }
    }
  }()

  A <- true // 启动条件
  <-Exit    // 结束条件
}

有疑问加站长微信联系(非本文作者))

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

2483 次点击  ∙  1 赞  
加入收藏 微博
2 回复  |  直到 2021-06-07 10:49:51
eachain
eachain · #1 · 4年之前
func main() {
    ch := make(chan struct{})
    run := func(i, n int) {
        for ; i < n; i += 2 {
            <-ch
            println(i)
            ch <- struct{}{}
        }
        if _, ok := <-ch; ok {
            close(ch)
        }
    }

    go run(1, 10)
    ch <- struct{}{}
    run(2, 10)
}
robin-go
robin-go · #2 · 4年之前
func main() {
    c := make(chan int)
    run := func() {
        for {
            i, ok := <-c
            if !ok {break}
            if i > 10 {
                close(c)
                break
            }
            fmt.Println(i)
            c <- i + 1
        }
    }
    go run()
    c <- 1
    run()
}
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传