一、程序中的通信方式GO语言中有一句名言:“不要用共享内存来通信,要用通信来共享内存”。在编程语言中,通信方式分为进程间通信和线程间通信。1、进程间通信,常用方法:命名管道未命名管道signalsharedmemorymessagequeuesemaphoresetsocket2,里面包含了一些协程,协程相当于用户态的轻量级线程,所以协程的大部分通信方式都可以通过线程间通信来完成。对于协程之间的通信方式,官方推荐使用channel,非常方便一对一协程之间的数据交换和通信。不过在一对多的广播场景下,就显得有些无力了,这时候就需要sync.Cond来辅助了。2、什么是广播?比如我上高中的时候,宿舍老师每天早上都要叫醒学生上课。这时,有两种解决办法:①叫醒各宿舍的学生。②在宿舍楼安装收音机,到了起床时间就用收音机叫醒学生。显然,使用广播的方式效率更高。编程中的广播可以理解为:多个操作进程依赖于一个操作进程的完成来执行某些动作,而这个依赖的操作进程在唤醒所有依赖时使用通知的方式。在Go语言中,可以使用sync.Cond来实现多个协程之间的广播通知功能。3、sync.Condcond是sync包下的一种数据类型,相当于线程间通信的条件变量方法。//Condimplementsaconditionvariable,arendezvouspoint//forgoroutineswaitingfororannouncingtheoccurrence//ofanevent.////EachCondhasanassociatedLockerL(oftena*Mutexor*RWMutex),//whichmustbeheldwhenchangingtheconditionand//whencallingtheWaitmethod.////ACondmustnotbecopiedafterfirstuse.typeCondstruct{noCopynoCopy//在第一次使用后不可复制,使用govet作为检测,使用//Lisheldwhileobservingorchangingthecondition//根据需求初始化不同的锁,比如*Mutex和*RWMutex。注意指针类型LLocker//带头指针和尾指针的链表。存储阻塞的协程,通知时操作链表中的协程notifynotifyListcheckercopyChecker//复制检查,检查cond实例是否被复制}该数据类型提供的方法有:typeCondfuncNewCond(lLocker)*Condfunc(c*Cond)Broadcast()//通知所有协程,broadcastfunc(c*Cond)Signal()//通知一个协程func(c*Cond)Wait()//阻塞等待直到被唤醒对应的源码追踪//Waitatomicallyunlocksc.Landsuspendsexecution//调用的goroutine。Afterlaterresumingexecution,//Waitlocksc.Lbeforereturning.Unlikeinothersystems,//WaitcannotreturnunlessawokenbyBroadcastorSignal.////Becausec.LisnotlockedwhenWaitfirstresumes,thecaller//typicallycannotassumethattheconditionistruewhen//Waitreturns.Instead,thecallershouldWaitinaloop:////注意下面的写法是官??方推荐的//c.L.Lock()//for!condition(){//c.Wait()////...makeuseofcondition...//c.L.Unlock()//func(c*Cond)Wait(){//检查c是否被复制,如果是,panicc.checker.check()//获取wai中的一个ticket值ting队列,作为唤醒时的token凭证t:=runtime_notifyListAdd(&c.notify)//Unlockc.L.Unlock()//注意,上面的ticket值将作为阻塞携程的标识//加入通知队列//这里执行gopark(),当前协程被挂起,直到有信号或广播发起通知runtime_notifyListWait(&c.notify,t)//被唤醒后,先获取锁c.L.Lock()}//Signalwakesonegoroutinewaitingonc,ifthereisany.////Itisallowedbutnotrequiredforthecallertoholdc.L//duringthecall.func(c*Cond)Signal(){c.checker.check()runtime_notifyListNotifyOne(&c.notify)//随机选择一个通知,waitunblocking}//Broadcastwakesallgoroutineswaitingonc.////Itsallowedbutnotrequiredforthecallertoholdc.L//duringthecall.func(c*Cond)Broadcast(){c.checker.check()//通知所有等待阻塞的协程//主要唤醒每个协程cond.notify列表上的coroutineruntime_notifyListNotifyAll(&c.notify)}使用方法,代码示例:varlockersync.Mutexvarcond=sync.NewCond(&locker)//NewCond(lLocker)定义了一个带有lock和unlock方法的接口//看到sync.Mutex方法,func(m*Mutex)Lock(),可以看到指针有这两个方法,所以指针要传给funcmain(){//启动多个协程fori:=0;i<10;i++{gofunc(xint){cond.L.Lock()//获取锁defercond.L.Unlock()//释放锁cond.Wait()//等待通知,阻塞当前goroutine//当通知到达,cond.Wait()将结束阻塞,做点什么。这里只打印fmt.Println(x)}(i)}time.Sleep(time.Second*1)//休眠1秒,等待所有Goroutine进入Wait阻塞状态fmt.Println("Signal...")cond.Signal()//1秒后,向已经获取锁的goroutine发送通知time.Sleep(time.Second*1)fmt.Println("Signal...")cond.Signal()//1秒后向已获取锁的goroutine发送下一个通知。Sleep(time.Second*1)cond.Broadcast()//1秒后向goroutine发送广播所有等待的goroutinesfmt.Println("Broadcast...")time.Sleep(time.Second*1)//等待所有goroutine执行完毕}总结Go中协程之间的通信方式有很多种,最常用的是channel。如果涉及多个协程通知,可以使用sync.Cond。查看channel和sync.Cond的源码后,你会发现它们有相似之处:阻塞协程统一封装在sudog结构体中。当头尾指针的单向链表存储阻塞,等待唤醒的协程阻塞时,使用gopark()挂起协程。虽然有相似之处,但也有本质区别:channel可以用于在协程之间传递数据sync。cond不能在协程之间传递数据,主要用于阻塞协程的唤醒操作。如果需要传递数据,需要全局变量传递
