go被誉为互联网的C语言,它的同步其实还是基于传统语言,但是它的优势在于语言层面实现了协程支持,goroutine和channel组成的CSP模型,大大减少了并发编程开发难度,简单记下心得供有需要朋友参考。
go中开辟新线程使用go 关键字即可,同步方法分为传统方法和基于通道(channel)的方法,通道包括无缓冲和有缓冲通道,这些同步方法结合保证了go并发编程的灵活性。
1.传统方法
传统方法包括有锁编程和无锁编程,有锁指使用锁来保证代码同步,无锁编程使用原子编程来保证代码同步。
如下,开辟两个go线程,同时做加操作,因为涉及到竞态条件,对于全局变量count=0结果为1.
func test_count1(name string){
defer g.Done()
fmt.Println(name, " run...")
v := count
runtime.Gosched()
v++
count=v
fmt.Println(name, "done... count=", v)
}
为了解决这个问题,就必须保证count增加过程中的不可被分割,使用加锁编程如下:
//锁同步
func test_count2(name string){
defer g.Done()
fmt.Println(name, " run...")
m.Lock()
v := count
runtime.Gosched()
v++
count=v
m.Unlock()
fmt.Println(name, "done... count=", v)
}
也可以使用原子编程如下:
//原子同步
func test_count3(name string){
defer g.Done()
fmt.Println(name, " run...")
atomic.AddInt32(&count, 1)
runtime.Gosched()
}
2.无缓冲通道
无缓冲通道保证数据发送和接收是同时的,适合用于多个线程并发执行且相互之间有一定互斥关系的,每个时刻只能有一个线程持有通道数据。
比如如下,两个选手相互击球,通道是球,每个时刻一定只有一个选手能持有球,通过将数据传给通道来模拟传球过程,这也是channel的特质——将数据同步转成goroutine间的数据传递。
//无缓冲通道1-球赛对打
func Play(name string, ball chan int){
defer w.Done()
for {
v, ok := <-ball
if !ok {
fmt.Println(name, " win the game.")
break
}
n := rand.Intn(100)
if n%13 == 0 {
fmt.Println(name, " lose the game.")
close(ball)
break
}
v++;
fmt.Println(name, " kick the ball.")
ball <- v
}
}
func testPlay(){
w.Add(2)
ball := make(chan int)
go Play("wenzhou", ball)
go Play("wenwei", ball)
ball <- 1
w.Wait()
}
还可以比喻成接力赛,每次只有一个选手能够获取接力球,如下,通过传递数据给通道来模拟选手接力的过程。
//无缓冲通道2-接力赛
func Run(ball chan int){
v, ok := <-ball
if !ok{
return
}
fmt.Printf("Runner %d start to run\n", v)
if v!=4 {
v++
fmt.Printf("Runner %d ready to run\n", v)
go Run(ball)
}
//Running
fmt.Println("...")
time.Sleep(time.Duration(1)*time.Second)
if v==4 {
fmt.Printf("Runner %d Finished, race over\n", v)
w.Done()
return
}
fmt.Printf("Runner %d give ball to Runner %d\n", v-1, v)
ball <- v
}
func testRun(){
w.Add(1)
ball := make(chan int)
go Run(ball)
ball <- 1
w.Wait()
}
3.有缓冲通道
有缓冲通道的可以起到缓存和队列的作用,如下采用通道来完成任务排队的功能,很轻易的实现多路处理器的功能。
//带缓冲通道
func Worker(task chan string, worker int){
defer w.Done()
for {
t, ok := <-task
if !ok {
fmt.Printf("Worker %d Shutdwon\n", worker)
break
}
fmt.Printf("Worker %d - Started:%s...\n", worker, t)
time.Sleep(time.Duration(rand.Intn(100))*time.Microsecond)
fmt.Printf("Worker %d - Completed:%s...\n", worker, t)
}
}
func testWorker(){
//缓冲通道
tasks := make(chan string, NUM_TASK)
//创建Worker,等待处理任务
w.Add(NUM_WORKER)
for i:=1; i<=NUM_WORKER; i++ {
go Worker(tasks, i)
}
//抛任务处理
for t:=1; t<=NUM_TASK; t++ {
tasks <- fmt.Sprintf("Task-%d", t)
}
//关闭通道,等待处理完毕,注意go机制保证关闭通道后剩余任务可以被处理完毕
close(tasks)
w.Wait()
}
演示代码下载链接
原创,转载请注明来自http://blog.csdn.net/wenzhou1219