• 企业400电话
  • 微网小程序
  • AI电话机器人
  • 电商代运营
  • 全 部 栏 目

    企业400电话 网络优化推广 AI电话机器人 呼叫中心 网站建设 商标✡知产 微网小程序 电商运营 彩铃•短信 增值拓展业务
    Go使用协程交替打印字符

    需求: 模拟两个协程,分别循环打印字母A和B。

    分析: 要实现两个协程之间的交替协作,就必须用到channel通信机制,而channel正好是同步阻塞的。

    半开方式

    首先我们用一个channel变量来控制两个goroutine的交替打印:

    func main() {
       exit := make(chan bool)
       ch1 := make(chan int)
       go func() {
          for i := 1; i = 10; i++ {
             ch1 - 0 //生产
             fmt.Println("A",i)
          }
          exit - true
       }()
       go func() {
          for i := 1; i = 10; i++ {
             -ch1 //消费
             fmt.Println("B",i)
          }
       }()
       -exit
    }
    

    结果发现打印出了ABBAABBA...的效果。

    也就是我们控制了开始的次序,但没有控制结束的次序,发生了并发不安全的情况。

    其实半开模式也可以用于某些场景下,如: 两个goroutine,在条件控制下,交替打印奇偶数:

    func main() {
       exit := make(chan bool)
       ch1 := make(chan int)
       go func() {
          for i := 1; i = 10; i++ {
             ch1 - 0
             if i%2 == 0 {
                fmt.Println("A", i)
             }
          }
          exit - true
       }()
       go func() {
          for i := 1; i = 10; i++ {
             -ch1
             if i%2 == 1 {
                fmt.Println("B", i)
             }
          }
       }()
       -exit
    }
    

    封闭方式

    接下来我们使用两个channel变量来模拟goroutine循环体的互斥问题。

    func main() {
       exit := make(chan bool)
       ch1, ch2 := make(chan bool), make(chan bool)
       
       go func() {
          for i := 1; i = 10; i++ {
             ch1 - true
             fmt.Println("A", i)
             //在ch1和ch2之间是阻塞独占的
             -ch2
          }
          exit - true
       }()
       go func() {
          for i := 1; i = 10; i++ {
             -ch1
             fmt.Println("B", i)
             ch2 - true
          }
       }()
       -exit
    }

    我们在循环体首尾都使用了阻塞独占模式,两个chan交替释放控制权,达到了安全的协程交互控制。

    再看看下面的Demo,同样的原理:

    func main(){
       ch1 :=make(chan int)
       ch2 :=make(chan string)
       str :=[5]string{"a","b","c","d","e"}
       go func() {
          for i:=0;i5;i++{
             ch1-i
             fmt.Print(i+1)
             -ch2
          }
       }()
       for _,v :=range str{
          -ch1
          fmt.Print(v)
          ch2-v
       }
    }
    

    缓冲模式

    缓冲模式和封闭模式相似,只是封闭模式中,两个goroutine有明确的首尾角色。而缓冲模式的第一生产者交给了主协程,两个goroutine结构一样,轮式交换角色。

    func main() {
       exit := make(chan bool)
       ch1, ch2 := make(chan bool,1), make(chan bool)
       ch1 - true //生产(选择一个启动项)
       
       go func() {
          for i := 1; i = 10; i++ {
             if ok := -ch1; ok { //消费
                fmt.Println("A", 2*i-1)
                ch2 - true //生产
             }
          }
       }()
       go func() {
          defer func() { close(exit) }()
          for i := 1; i = 10; i++ {
             if ok := -ch2; ok { //消费
                fmt.Println("B", 2*i)
                ch1 - true //生产
             }
          }
       }()
       -exit
    }
    

    结论:

    Channel的本质就是同步式的生产消费模式

    补充:go 让N个协程交替打印1-100

    今天遇到一道面试题,开启N个协程,并交替打印1-100如给定N=3则输出:

    goroutine0: 0

    goroutine1: 1

    goroutine2: 2

    goroutine0: 3

    goroutine1: 4

    面试时没答案,虽过后研究参考了一些网上方法,并记录下来,先上代码

    func print() {
    	chanNum := 3                           // chan 数量
    	chanQueue := make([]chan int, chanNum) // 创建chan Slice
    	var result = 0                         // 值
    	exitChan := make(chan bool)            // 退出标识
    	for i := 0; i  chanNum; i++ {
    		//	创建chan
    		chanQueue[i] = make(chan int)
    		if i == chanNum-1 {
    			//	给最后一个chan写一条数据,为了第一次输出从第1个chan输出
    			go func(i int) {
    				chanQueue[i] - 1
    			}(i)
    		}
    	}
    	for i := 0; i  chanNum; i++ {
    		var lastChan chan int //    上一个goroutine 结束才能输出 控制输出顺序
    		var curChan chan int  //	当前阻塞输出的goroutine
    		if i == 0 {
    			lastChan = chanQueue[chanNum-1]
    		} else {
    			lastChan = chanQueue[i-1]
    		}
    		curChan = chanQueue[i]
    		go func(i int, lastChan, curChan chan int) {
    			for {
    				if result > 100 {
    					//	超过100就退出
    					exitChan - true
    				}
    				//	一直阻塞到上一个输出完,控制顺序
    				-lastChan
    				fmt.Printf("thread%d: %d \n", i, result)
    				result = result + 1
    				//	当前goroutine已输出
    				curChan - 1
    			}
    		}(i, lastChan, curChan)
    	}
    	-exitChan
    	fmt.Println("done")
    }
    

    1、第一个for循环创建chan

    2、第二个for循环里的lastChan意思是,当前chan如果要打印数据,就必须得上一个chan打印完后才能打印。

    这里假设N=2,chan索引为0,1,当索引1要输出,就阻塞到索引0的chan有数据为止,当自己打印完后往自己的chan中发送一个1,方便给依赖自己的chan 解除阻塞。

    这里有个特殊的地方,当索引为0时,他的依赖索引chan就为chanQueue的长度-1,如果没有在创建Chan中的时候没有下面这一串代码就会造成死锁

    if i == chanNum-1 {
     // 给最后一个chan写一条数据,为了第一次输出从第1个chan输出
     go func(i int) {
     chanQueue[i] - 1
     }(i)
    }

    以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。如有错误或未考虑完全的地方,望不吝赐教。

    您可能感兴趣的文章:
    • Go 并发实现协程同步的多种解决方法
    • go等待一组协程结束的操作方式
    • golang协程池模拟实现群发邮件功能
    • 解决go在函数退出后子协程的退出问题
    • Golang 之协程的用法讲解
    • go 协程返回值处理操作
    • 浅谈golang for 循环中使用协程的问题
    • Go并发:使用sync.WaitGroup实现协程同步方式
    上一篇:golang goroutine顺序输出方式
    下一篇:使用Golang的channel交叉打印两个数组的操作
  • 相关文章
  • 

    © 2016-2020 巨人网络通讯 版权所有

    《增值电信业务经营许可证》 苏ICP备15040257号-8

    Go使用协程交替打印字符 使用,协程,交替,打印,字符,