Skip to content

1.交替打印数字和字母 #104

@MagicalBridge

Description

@MagicalBridge

问题描述:

使⽤两个 goroutine 交替打印序列,⼀个 goroutine 打印数字, 另外⼀个 goroutine 打印字⺟, 最终效果如下:

12AB34CD56EF78GH910IJ1112KL1314MN1516OP1718QR1920ST2122UV2324WX2526YZ2728

解题思路:

使⽤ channel 来控制打印的进度。使⽤两个 channel,来分别控制数字和字⺟的打印序列, 数字打印完成后通过 channel 通知字⺟打印, 字⺟打印完成后通知数字打印,然后周⽽复始的⼯作。

源码参考:

package main

import (
    "fmt"
    "sync"
)

func main() {
    // 创建两个无缓冲的channel, 用于goroutine之间的通信
    letter, number := make(chan bool), make(chan bool)
    // 创建一个WaitGroup, 用于等待goroutine 完成
    wait := sync.WaitGroup{}

    // 启动一个goroutine, 用于打印数字
    go func() {
       // 初始化变量i为1, 用于记录当前打印的数字
       i := 1
       // 无限循环,不停地打印数字
       for {
          // 使用select语句, 监听channel的状态
          select {
          case <-number:
             fmt.Print(i)
             i++
             fmt.Print(i)
             i++
             // 向letter channel发送信号, 通知打印字母的goroutine可以继续执行
             letter <- true
          }
       }
    }()

    // 将 WaitGroup 的计数器加1, 表示有一个goroutine需要等待
    wait.Add(1)

    // 启动一个goroutine, 用于打印字母
    go func(wait *sync.WaitGroup) {
       // 初始化一个变量i,用于记录当前打印的字母
       i := 'A'
       for {
          select {
          // 从letter channel中接收信号, 表示可以打印字母了
          case <-letter:
             // 如果已经打印完了所有的字母, 则调用WaitGroup的Done方法, 表示当前goroutine已经完成, 然后返回
             if i >= 'Z' {
                wait.Done()
                return
             }
             fmt.Print(string(i))
             i++
             fmt.Print(string(i))
             i++
             // 向number channel发送信号, 通知打印数字的goroutine可以继续执行
             number <- true
          }
       }
    }(&wait)

    // 向number channel发送信号, 表示可以开始打印数字了
    number <- true

    // 等待所有的goroutine完成
    wait.Wait()
}

一些值得讨论的细节:

1、这段代码中的两个 go goroutine 会交替执行,因为它们是通过无缓冲的 channel letter 和 number 进行通信,当一个 goroutine 打印完数字或字母后,它会通过相应的 channel 通知另一个 goroutine 继续执行。由于 main 函数中首先向 number channel 发送了一个信号,因此打印数字的 goroutine 会先开始执行。然后,打印字母的 goroutine 会在接收到 letter channel 的信号后开始执行。在打印字母的 goroutine 中,当变量 i 大于等于 'Z' 时,它会调用 wait.Done() 表示自己已经完成,然后返回。此时,wait.Wait() 会停止阻塞,main 函数会继续执行并结束。

2、在Go语言中,fmt.Print 函数接受一个或多个参数,并将这些参数打印到标准输出。当你直接将一个字符类型的值(如 i := 'A')传递给 fmt.Print 时,它会尝试将这个字符转换为其对应的整数值,然后打印这个整数值。这是因为在Go语言中,字符类型(rune)实际上是一个整数类型,它表示Unicode字符的代码点。

如果你想要打印字符本身而不是它的整数值,你需要将字符转换为字符串类型。这就是为什么 fmt.Print(string(i)) 能够正确打印字符的原因。在这个调用中,string(i) 将字符 i 转换为字符串类型,然后 fmt.Print 函数将字符串打印到控制台。

3、打印数字的 goroutine 在启动后会进入一个无限循环,在这个循环中它会不断地等待从 number 通道接收到信号。一旦接收到信号,它就会执行相应的操作,即打印两个连续的数字,然后向 letter 通道发送一个信号,通知打印字母的 goroutine 可以继续执行。

在这个过程中,打印数字的 goroutine 会一直阻塞在 select 语句的 case <-number: 分支上,直到 number 通道接收到数据。这是因为 number 通道是一个无缓冲的通道,这意味着如果没有其他 goroutine 向 number 通道发送数据,那么从 number 通道接收数据的操作就会一直阻塞。打印数字的 goroutine 会一直等待,直到它从 number 通道接收到信号。这个信号通常是由打印字母的 goroutine 在完成两个字母的打印后发送的。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions