go协程奇偶打印和交替打印ABC
前言
这个题目算是面试常问了,之前用JAVA多线程也经常问到 Java多线程打印ABC。
奇偶打印
代码直接参考 https://blog.csdn.net/nxj_climb/article/details/133035078, 可能有些小更改 但大差不差。
就是使用channel对协程读和写进行阻塞来保证时序。
(note: 也可以参照上述博客的地址,另一位博主也show了一下单个channel打印奇偶的写法,不过为了普适性,方便应对面试扩展题,还是掌握一下通用一些的写法吧。
packagemainimport("fmt""sync")funcprintNumber(nint){c1:=make(chanbool,1)c2:=make(chanbool,1)wg:=sync.WaitGroup{}wg.Add(2)gofunc(nint){deferwg.Done()fori:=1;i<=n;i+=2{<-c1 fmt.Println(fmt.Sprintf("A %d",i))c2<-true}}(n)gofunc(nint){deferwg.Done()fori:=2;i<=n;i+=2{<-c2 fmt.Println(fmt.Sprintf("B %d",i))c1<-true}}(n)// 启动第一个goroutine,因为从1开始,所以先启动打印奇数的协程c1<-truewg.Wait()}funcmain(){printNumber(10)}PrintABC
模仿刚刚的策略,书写printABC的策略。使用三个通道进行阻塞读写
缓冲channel
packagemainimport("fmt""sync")// 交替打印n次ABCfuncPrintABC(nint){c1:=make(chanbool,1)// 记得一定是一个,不然没法阻塞写c2:=make(chanbool,1)c3:=make(chanbool,1)wg:=sync.WaitGroup{}wg.Add(3)// 3个协程gofunc(nint){deferwg.Done()fori:=1;i<=n;i++{<-c1 fmt.Print(fmt.Sprintf("%d A",i))// 为了不去数多少个,加个数字吧// fmt.Print("A")c2<-true}}(n)gofunc(nint){deferwg.Done()fori:=1;i<=n;i++{<-c2 fmt.Print("B")c3<-true}}(n)gofunc(nint){deferwg.Done()fori:=1;i<=n;i++{<-c3 fmt.Println("C")c1<-true}}(n)c1<-truewg.Wait()}funcmain(){PrintABC(10)}封装一下
packagemainimport("fmt""sync")// Printer 结构体封装打印逻辑typePrinterstruct{chars[]string// 要打印的字符countint// 每个字符打印次数chs[]chanstruct{}// 控制通道wg sync.WaitGroup totalRoundsint// 总轮次}// NewPrinter 创建新的打印机funcNewPrinter(chars[]string,countint)*Printer{n:=len(chars)chs:=make([]chanstruct{},n)fori:=rangechs{chs[i]=make(chanstruct{})}return&Printer{chars:chars,count:count,chs:chs,totalRounds:len(chars)*count,}}// Start 启动所有打印协程func(p*Printer)Start(){n:=len(p.chars)p.wg.Add(n)// 为每个字符创建一个协程fori:=0;i<n;i++{gop.printWorker(i)}// 启动第一个协程p.chs[0]<-struct{}{}p.wg.Wait()fmt.Println()}// printWorker 打印工作协程func(p*Printer)printWorker(idint){deferp.wg.Done()// 计算该协程需要打印的次数rounds:=p.totalRounds/len(p.chars)forj:=0;j<rounds;j++{<-p.chs[id]// 等待自己的信号fmt.Print(p.chars[id])// 打印字符nextID:=(id+1)%len(p.chars)// 计算下一个协程的IDifj<rounds-1||nextID!=0{// 如果不是最后一轮或最后一个字符p.chs[nextID]<-struct{}{}// 通知下一个协程}}}funcmain(){// 示例1:打印 ABC 10次fmt.Print("ABC打印10次: ")printer1:=NewPrinter([]string{"A","B","C"},10)printer1.Start()// 示例2:打印 1234 5次fmt.Print("1234打印5次: ")printer2:=NewPrinter([]string{"1","2","3","4"},5)printer2.Start()// 示例3:打印 XYZ 3次fmt.Print("XYZ打印3次: ")printer3:=NewPrinter([]string{"X","Y","Z"},3)printer3.Start()}ABC打印10次: ABCABCABCABCABCABCABCABCABCABC
1234打印5次: 12341234123412341234
XYZ打印3次: XYZXYZXYZ
sync.cond
packagemainimport("fmt""sync")funcmain(){varmu sync.Mutex cond:=sync.NewCond(&mu)current:=0// 0-A, 1-B, 2-Ccount:=10done:=falsevarwg sync.WaitGroup wg.Add(3)// 协程 Agofunc(){deferwg.Done()fori:=0;i<count;i++{mu.Lock()forcurrent!=0&&!done{cond.Wait()}fmt.Print("A")current=1mu.Unlock()cond.Broadcast()}}()// 协程 Bgofunc(){deferwg.Done()fori:=0;i<count;i++{mu.Lock()forcurrent!=1&&!done{cond.Wait()}fmt.Print("B")current=2mu.Unlock()cond.Broadcast()}}()// 协程 Cgofunc(){deferwg.Done()fori:=0;i<count;i++{mu.Lock()forcurrent!=2&&!done{cond.Wait()}fmt.Print("C")ifi==count-1{done=true}current=0mu.Unlock()cond.Broadcast()}}()wg.Wait()fmt.Println()}