golang协程同步的几种方法

BethlyRoseDaisley · · 7297 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

# golang协程同步的几种方法 --- 本文简要介绍下go中协程的几种同步方法。 ## 协程概念简要理解 协程类似线程,是一种更为轻量级的调度单位,但协程还是不同于线程的,线程是系统级实现的,常见的调度方法是时间片轮转法,如每隔10ms切换一个线程执行。 协程则是应用软件级实现,它和线程的原理差不多,当一个协程调度到另一个协程时,将上一个协程的上下文信息压入堆栈,来回切换。一个线程可以跑很多个协程,由这个线程来调度协程的切换,如果是C/C++的话底层就能通过select/poll/epoll来做,例如微信后台的开源libco库。 golang协程底层不是C,纯go实现,golang的协程应该是目前各类有协程概念的语言中实现的最完整和成熟的,调度是基于GPM模型实现的,有兴趣可以去了解下,这里不扯远了,下面看看协程的同步。 ## 为什么要做同步 至于为什么需要同步呢,类似线程要做同步差不多,现在的cpu都是多核,假设一核一个线程同时一起访问同一块内存中的数据吗,那么可能上一ns第一个线程刚把数据从寄存器拷贝到内存,第二个线程马上又把此数据用它修改的值给覆盖了,这样共享数据变会乱套。 举个例子 : 用2个协程序并发各自增一个全局变量100 0000 次 ```go package main import( "fmt" "time" ) var share_cnt uint64 = 0 func incrShareCnt() { for i:=0; i < 1000000; i++ { share_cnt++ } fmt.Println(share_cnt) } func main() { for i:=0; i < 2; i++ { go incrShareCnt() } time.Sleep(1000*time.Second) } ``` 运行4次 , 可以看到我们虽然自增了200 0000次,但没有一个输出200 0000的结果. ![image.png](https://static.studygolang.com/190629/32bc8a5cfe6d4fc32207f7aadb6367dd.png) ## 协程的几种同步方法 ### Mutex 互斥锁,可以创建为其他结构体的字段;零值为解锁状态。Mutex类型的锁和线程无关,可以由不同的线程加锁和解锁。 ```go package main import( "fmt" "time" "sync" ) var share_cnt uint64 = 0 var lck sync.Mutex func incrShareCnt() { for i:=0; i < 1000000; i++ { lck.Lock() share_cnt++ lck.Unlock() } fmt.Println(share_cnt) } func main() { for i:=0; i < 2; i++ { go incrShareCnt() } time.Sleep(1000*time.Second) } ``` ![image.png](https://static.studygolang.com/190629/ed3439ec45c88209799054405e7ac9a3.png) ### channel 使用golang的channel, 下面一个典型的生产消费模型 ```go package main import( "fmt" "time" "strconv" ) func main() { msg_chan := make(chan string) done := make(chan bool) i := 0 go func() { for { i++ time.Sleep(1*time.Second) msg_chan <- "on message" <- done } }() go func() { for { select { case msg := <- msg_chan : i++ fmt.Println(msg + " " + strconv.Itoa(i)) time.Sleep(2*time.Second) done <- true } } }() time.Sleep(20*time.Second) } ``` ![image.png](https://static.studygolang.com/190629/48b904b903dd2468b818cfb6df0fee1c.png) ### WaitGroup sync包中的WaitGroup可用等待一组协程的结束。 父协程通过Add方法来设定应等待的线程的数量。 每个被等待的协程在结束时调用Done方法。 同时,主协程里调用Wait方法阻塞至所有线程结束。 ```go package main import( "sync" "net/http" ) var wg sync.WaitGroup var urls = []string{ "http://www.baidu.com/", "http://www.taobao.com/", "http://www.tianmao.com/", } func main() { for _, url := range urls { // Increment the WaitGroup counter. wg.Add(1) // Launch a goroutine to fetch the URL. go func(url string) { // Decrement the counter when the goroutine completes. defer wg.Done() // Fetch the URL. http.Get(url) }(url) } // Wait for all HTTP fetches to complete. wg.Wait() } ```

有疑问加站长微信联系(非本文作者))

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

7297 次点击  
加入收藏 微博
被以下专栏收入,发现更多相似内容
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传