竞争条件和数据竞争 当两个或多个操作必须以正确的顺序执行时,就会出现竞争条件,但程序尚未编写为保证维护此顺序。 数据竞争是指一个并发操作试图读取一个变量,而在某个不确定的时间另一个并发操作试图写入同一个变量。mainfunc是主goroutine。funcmain(){vardataintgofunc(){data}()ifdata0{fmt。Printf(thevalueisd,data)}}sync内存访问同步 sync包包含对低级内存访问同步最有用的并发原语。临界区是代码中可以访问共享内存的地方mutex互斥体 Mutex代表互斥,是一种保护程序关键部分的方法。typeCounterstruct{musync。Mutexvalueint}func(cCounter)Increment(){c。mu。Lock()deferc。mu。Unlock()c。value}waitgroup等待组 调用添加一组goroutinesvarwgsync。WaitGroupfor,salutation:range〔〕string{hello,greetings,goodday}{wg。Add(1)gofunc(salutationstring){deferwg。Done()fmt。Println(salutation)}(salutation)}wg。Wait()读写互斥锁 更细粒度的内存控制,可以请求只读锁producer:func(wgsync。WaitGroup,lsync。Locker){deferwg。Done()fori:5;i0;i{l。Lock()l。Unlock()time。Sleep(1)}}observer:func(wgsync。WaitGroup,lsync。Locker){deferwg。Done()l。Lock()deferl。Unlock()}test:func(countint,mutex,rwMutexsync。Locker)time。Duration{varwgsync。WaitGroupwg。Add(count1)beginTestTime:time。Now()goproducer(wg,mutex)fori:count;i0;i{goobserver(wg,rwMutex)}wg。Wait()returntime。Since(beginTestTime)}tw:tabwriter。NewWriter(os。Stdout,0,1,2,,0)defertw。Flush()varmsync。RWMutexfmt。Fprintf(tw,ReadersRWMutexMutex)fori:0;i20;i{count:int(math。Pow(2,float64(i)))fmt。Fprintf(tw,dvv,count,test(count,m,m。RLocker()),test(count,m,m),)}cond条件 如果有某种方法可以让goroutine有效地休眠,直到收到唤醒并检查其状态的信号,那就更好了。这正是Cond类型为我们所做的。 Cond和Broadcast是用于通知在Wait调用中阻塞的goroutines条件已被触发的方法。typeButtonstruct{Clickedsync。Cond}funcmain(){button:Button{Clicked:sync。NewCond(sync。Mutex{}),}runningongoroutineeveryfunctionthatpassedregisteredandwait,notexituntilthatgoroutineisconfirmedtoberunningsubscribe:func(csync。Cond,paramstring,fnfunc(sstring)){vargoroutineRunningsync。WaitGroupgoroutineRunning。Add(1)gofunc(pstring){goroutineRunning。Done()c。L。Lock()criticalsectiondeferc。L。Unlock()fmt。Println(Registeredandwait。。。)c。Wait()fn(p)}(param)goroutineRunning。Wait()}varclickRegisteredsync。WaitGroupfor,v:range〔〕string{Maximizingwindow。,Displayingannoyingdialogbox!,Mouseclicked。}{clickRegistered。Add(1)subscribe(button。Clicked,v,func(sstring){fmt。Println(s)clickRegistered。Done()})}button。Clicked。Broadcast()clickRegistered。Wait()}Once 确保即使在多个goroutine之间也只执行一次varcountintincrement:func(){count}varoncesync。Oncevarincrementssync。WaitGroupincrements。Add(100)fori:0;i100;i{gofunc(){deferincrements。Done()once。Do(increment)}()}increments。Wait()fmt。Printf(Countisd,count)Pool 管理连接池,数量packagemainimport(fmtsync)funcmain(){myPool:sync。Pool{New:func()interface{}{fmt。Println(Creatingnewinstance。)returnstruct{}{}},}GetcallNewfunctiondefinedinpoolifthereisnoinstancestartedmyPool。Get()instance:myPool。Get()fmt。Println(instance,instance)hereweputapreviouslyretrievedinstancebackintothepool,thisincreasesthenumberofinstancesavailabletoonemyPool。Put(instance)whenthiscallisexecuted,wewillreusethepreviouslyallocatedinstanceandputitbackinthepoolmyPool。Get()varnumCalcsCreatedintcalcPool:sync。Pool{New:func()interface{}{fmt。Println(newcalcpool)numCalcsCreated1mem:make(〔〕byte,1024)returnmem},}fmt。Println(calcPool。New,calcPool。New())calcPool。Put(calcPool。New())calcPool。Put(calcPool。New())calcPool。Put(calcPool。New())calcPool。Put(calcPool。New())calcPool。Get()constnumWorkers10241024varwgsync。WaitGroupwg。Add(numWorkers)fori:numWorkers;i0;i{gofunc(){deferwg。Done()mem:calcPool。Get()。(〔〕byte)defercalcPool。Put(mem)Assumesomethinginteresting,butquickisbeingdonewiththismemory。}()}wg。Wait()fmt。Printf(dcalculatorswerecreated。,numCalcsCreated)}死锁 死锁是其中所有并发进程都在等待彼此。packagemainimport(fmtsynctime)typevaluestruct{musync。Mutexvalueint}funcmain(){varwgsync。WaitGroupprintSum:func(v1,v2value){deferwg。Done()v1。mu。Lock()deferv1。mu。Unlock()deadlocktime。Sleep(2time。Second)v2。mu。Lock()deferv2。mu。Unlock()fmt。Printf(sumv,v1。valuev2。value)}vara,bvaluewg。Add(2)goprintSum(a,b)goprintSum(b,a)wg。Wait()} 总结 本文是go语言并发编程指南最佳实践第一篇,后续第二篇还会整理各种channel的特性,锁的使用在并发编程中的特点与各种用途举例,全部都是最接地气的代码示例。关注我,敬请期待下一篇。