Golang
主页 > 脚本 > Golang >

GO利用channel协调协程的实现介绍

2023-05-25 | 佚名 | 点击:

go 当中的并发编程是通过goroutine来实现的,利用channel(管道)可以在协程之间传递数据,实现协程的协调与同步。

使用

新建一个管道,使用make channel 来构建

1

2

3

4

5

6

7

8

// 构建一个缓存长度为8 的管道

ch := make(chan int ,8)

// 写入

ch <- 10

// 取出

number := <-ch

// 关闭

close(ch)

注意??: 取数据的时候,如果没得取,会阻塞代码的执行,如果一直没有取到,那就是死锁

实现生产者消费者模式

两个生产者者协程和一个消费者协程

使用waitGroup

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

func main() {  

    ch := make(chan int, 100)  

    wg := sync.WaitGroup{}  

    wg.Add(2)  

    // 生产者

    go func() {  

        defer wg.Done()  

        // 写入数据  

        for i := 0; i < 10; i++ {  

            ch <- i  

        }  

    }()  

    // 生产者

    go func() {  

        defer wg.Done()  

        // 写入数据  

        for i := 0; i < 10; i++ {  

            ch <- i  

        }  

    }()  

    wg2 := sync.WaitGroup{}  

    wg2.Add(1)  

    // 消费者

    go func() {  

        sum := 0  

        fmt.Printf("sum %d \n", sum)  

        for {  

            // 这里会等待  

            temp, ok := <-ch  

            // close 并且 管道为空,ok = false  

            if !ok {    

                break  

            } else {  

                sum += temp  

            }  

        }  

        fmt.Printf("sum %d \n", sum)  

        wg2.Done()  

    }()  

    // 等待俩生产者结束  

    wg.Wait()  

    // 生产数据之后,消费者也并行读完了,此时可以关闭 管道 来 跳出for循环了 

    close(ch)  

    // 等待消费者协程结束  

    wg2.Wait()  

}

使用管道则将wg2相关的代码改掉

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

func main() {  

    //... 

    //...

    ch2 := make(chan struct{}, 0)  

    go func() {  

        sum := 0  

        fmt.Printf("sum %d \n", sum)  

        for {  

            // 这里会等待  

            temp, ok := <- ch  

            // close 并且 管道为空,ok = false  

            if !ok {  

                break  

            } else {  

                sum += temp  

            }  

        }  

        fmt.Printf("sum %d \n", sum)  

        ch2 <- struct{}{}  

    }()  

    // 等待俩生产者结束  

    wg.Wait()  

    // 关闭管道

    close(ch)  

    // 等待消费者协程结束  

    <-ch2

}

实战面试题: 「交替打印数字和字母」

题目

使用两个 goroutine 交替打印序列,一个 goroutine 打印数字, 另外一个 goroutine 打印字母, 最终效果如下:

12AB34CD56EF78GH910IJ1112KL1314MN1516OP1718QR1920ST2122UV2324WX2526YZ2728

解题思路

利用channel的 阻塞 来协调线程,达到线程交叉执行的效果。

代码

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

func main() { 

    letter, number := make(chan bool), make(chan bool) 

    wait := sync.WaitGroup{} 

    go func() { 

        i := 1 

        for { 

            if <-number { 

                fmt.Print(i) 

                i++ 

                fmt.Print(i) 

                i++ 

                letter <- true 

            } 

        } 

    }() 

    wait.Add(1) 

    go func() { 

        // 获得ASCII码 

        i := 'A' 

        for { 

            if <-letter { 

                // 当前已经超过Z时,无需再打印

                if i > 'Z' { 

                    // 停止等待,并且跳出循环

                    wait.Done() 

                    break 

                } 

                // 将ASCII码强转成字母输出 

                fmt.Print(string(i)) 

                i++ 

                fmt.Print(string(i)) 

                i++ 

                number <- true 

            } 

        } 

    }() 

    // 放行数字打印的阻塞

    number <- true 

    // 等待关闭主线程

    wait.Wait() 

}

其实完全也可以将waitGroup换成管道

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

func main() { 

    letter, number := make(chan bool), make(chan bool) 

    // 再定义一个管道来等待,替代waitGroup的作用

    wait := make(chan bool)

    // 打印数字

    go func() { 

        i := 1 

        for { 

            if <-number { 

                fmt.Print(i) 

                i++ 

                fmt.Print(i) 

                i++ 

                letter <- true 

            } 

        } 

    }() 

    // 打印字母

    go func() { 

        // 获得ASCII码 

        i := 'A' 

        for { 

            if <-letter { 

                if i > 'Z' { 

                    wait <- true 

                    break 

                } 

                // 将ASCII码强转成字母输出 

                fmt.Print(string(i)) 

                i++ 

                fmt.Print(string(i)) 

                i++ 

                number <- true 

            } 

        } 

    }() 

    number <- true 

    // 等待管道取值

    <- wait

}

原文链接:
相关文章
最新更新