go语言中如何用一个channel实现多个gorouting同步?

代码如下:

package main

import (
    "fmt"
    "time"
)

func main() {
    channelShunxu()
}

/**
通过 channel 控制实现顺序输出
*/
func channelShunxu() {
    ch1 := make(chan int)

    go printHello1(ch1)
    go printSpace1(ch1)
    go printWorld1(ch1)
    go printLn1(ch1)

    //这里休息一秒,否则的话,主协程直接退出了,不会等待子协程输出
    time.Sleep(time.Second)
}

func printHello1(ch1 chan int) {
    fmt.Print("hello")
    ch1 <- 1
}

func printSpace1(ch1 chan int) {
    <-ch1
    fmt.Print(" ")
    //<- ch1 若放在这里还是有可能出错
}

func printWorld1(ch1 chan int) {
    fmt.Print("world")
    ch1 <- 1
}

func printLn1(ch1 chan int) {
    <-ch1
    fmt.Println("")
}

我想问下能否只通过 ch1 控制这四个协程顺序的输入 hello world 吗?目前我这样写好像不行,跪求大神解答,谢谢!

经过总结各位大佬的意见以及自己的测试,最终我自己搞出了下面这种方法,请各位大佬点评!

/**
通过 channel 控制实现顺序输出
*/
func channelShunxu() {
    ch1 := make(chan int, 1)

    ch1 <- 1
    Loop:
    for {
        select {
        case value := <-ch1:
            if value == 1 {
                go printHello1(ch1)
            } else if value == 2 {
                go printSpace1(ch1)
            } else if value == 3 {
                go printWorld1(ch1)
            } else if value == 4 {
                go printLn1(ch1)
            } else if value == 5 {
                close(ch1)
                break Loop
            }
        }
    }

    //这里休息一秒,否则的话,主协程直接退出了,不会等待子协程输出
    time.Sleep(time.Second * 1)
}

func printHello1(ch1 chan int) {
    fmt.Print("hello")
    ch1 <- 2
}

func printSpace1(ch1 chan int) {
    fmt.Print(" ")
    ch1 <- 3
}

func printWorld1(ch1 chan int) {
    fmt.Print("world")
    ch1 <- 4
}

func printLn1(ch1 chan int) {
    fmt.Println("")
    ch1 <- 5
}
阅读 2.5k
3 个回答

建议在通道中传输序号来判断:
具体实现看Loop函数
之所以要设置f func()这个参数,是确保fmt.Print打印结果不乱。

package main

import (
    "fmt"
    "time"
)

func main() {
    channelShunxu()
}

/**
通过 channel 控制实现顺序输出
*/
func channelShunxu() {
    ch1 := make(chan int)

    // 就算打乱、休眠也无所谓
    go printHello1(ch1)
    go printSpace1(ch1)
    go printWorld1(ch1)
    go printLn1(ch1)

    time.Sleep(1 * time.Second)
}

func printHello1(ch1 chan int) {
    fmt.Print("hello")
    ch1 <- 1
}

func printSpace1(ch1 chan int) {
    Loop(ch1, func() {
        fmt.Print(" ")
    }, 1, 2)
}

func printWorld1(ch1 chan int) {
    Loop(ch1, func() {
        fmt.Print("world")
    }, 2, 3)
}

func printLn1(ch1 chan int) {
    Loop(ch1, func() {
        fmt.Println("")
    }, 3)
}

/*
ch:用于控制的通道
f:待执行的函数
target:目标序号
next:下一个序号(不输入,则代表结束)
*/
func Loop(ch chan int, f func(), target int, next ...int) {
    for {
        select {
        case i := <-ch:
            if i == target {
                f()
                if len(next) > 0 {
                    ch <- next[0]
                }
                return
            }
            // 还回去
            ch <- i
        }
    }
}

每一个goroutine中都使用for-select。然后channel中默认先放进去一个1。以第一个hello为例,当从管道拿到一个数字,是自己想要的,那就打印然后按顺序放入下一个数并退出;如果不是自己想要的,那就把这个已经消费掉的值再重新扔进管道就好了(扔进去后可以让自己休眠很小一段时间避免一直重复拿到)。

通过多个无缓冲 channel,让 goroutine 之间产生依赖,从而实现顺序输出。

package main

import (
    "fmt"
)

func main() {
    for i := 0; i < 10; i++ {
        channelShunxu()
    }
}

/**
通过 channel 控制实现顺序输出
*/
func channelShunxu() {
    ch1 := make(chan int)
    ch2 := make(chan int)
    ch3 := make(chan int)
    ch4 := make(chan int)

    go printHello1(ch1)
    go printSpace1(ch1, ch2)
    go printWorld1(ch2, ch3)
    go printLn1(ch3, ch4)

    <- ch4
    fmt.Println("end!")
}

func printHello1(in chan int) {
    fmt.Print("1")
    in <- 1
}

func printSpace1(out, in chan int) {
    <- out
    fmt.Print("2")
    in <- 1
}

func printWorld1(out, in chan int) {
    <- out
    fmt.Print("3")
    in <- 1
}

func printLn1(out, in chan int) {
    <- out
    fmt.Print("4")
    in <- 1
}
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题