经过Mutex和信道处理竞态条件。并发
当程序并发运行时,多个协程不该该同时访问那些修改共享资源的代码,这些修改共享资源的代码称为临界区。线程
Go中经过Mutex能够避免同时访问临界区,从而避免了竞态条件。code
Mutex提供了一种锁机制,能够确保某个时刻只有一个协程在临界区运行,防止出现竞态条件。server
Mutex中定义了两个方法:Lock和Unlock。 Lock和Unlock之间的代码只能由一个协程执行,避免了竞态条件。协程
mutex.Lock() x = x+1 mutex.Unlock()
Var x = 0 func increment(wg *sync.WaitGroup, m *sync.Mutex){ m.Lock() x = x+1 m.Unlock() wm.Done() } func main(){ var w sync.WaitGroup var m sync.Mutex for I := 0; I < 1000; I++{ w.Add(1) go increment(&w, &m) } w.Wait() }
Select用于在多个发送/接收信道中进行选择,select语句会一直阻塞,直到发送/接收操做准备就绪。资源
若是有多个信道操做准备就绪,select会随机选择其中之一执行。rem
output1 := make(chan string) output2 := make(chan string) go server1(output1) go server2(output2) select{ case s1 := <-output1: print case s2:= <-output2: print }
select 会一直阻塞,除非某个case准备就绪。string
经过select中的case能够避免信道死锁,由于case表明有没有就绪。it
若是一个select没有任何case,会一直阻塞,致使死锁。select
WaitGroup用于等待一批Go协程执行结束,程序控制会一直阻塞,直到这些协程所有执行完毕才会终止。
func process(I int, wg *sync.WaitGroup){ // do something wg.Done() // 让计数器减一 } func main(){ no := 3 var wg sync.WaitGroup for I := 0; I < no; I++{ wg.Add(1) // 循环3次计数器变成3 go process(I, &wg) // 传递地址很重要,若是不传递地址,每一个协程都会获得一个WaitGroup的值拷贝,这样就不能经过一个计数器控制协做了 } wg.Wait() // 等待主线程变成0 }
WaitGroup使用计数器来工做。调用WaitGroup的Add并传递一个int,作累加,减小可调用Done方法。 Wait()方法会阻塞调用WaitGroup的协程,直到计数器为0后才会中止阻塞。