GoLang之Concurrency再讨论

前端之家收集整理的这篇文章主要介绍了GoLang之Concurrency再讨论前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

0 goroutine是否并发的问题

GoLang通过go关键字实现并发操作(真的并发吗?),一个最简单的并发模型:

  1. packagemain
  2. import(
  3. "fmt"
  4. "math/rand"
  5. "time"
  6. )
  7. funcroutine(namestring,delaytime.Duration){
  8. t0:=time.Now()
  9. fmt.Println(name,"startat",t0)
  10. //停留xxx秒
  11. time.Sleep(delay)
  12. t1:=time.Now()
  13. fmt.Println(name,54)">"endat",t1)
  14. //计算时间差
  15. fmt.Println(name,54)">"lasted",t1.Sub(t0))
  16. }
  17. funcmain(){
  18. //生成随机种子,类似C语言中的srand((unsigned)time(0))生成随机种子
  19. rand.Seed(time.Now().Unix())
  20. //ToconvertanintegernumberofunitstoaDuration,multiply
  21. fmt.Println(time.Duration(5)*time.Second)
  22. varnamestring
  23. fori:=0;i<3;i++{
  24. name=fmt.Sprintf("go_%02d",i)//生成ID
  25. //生成随机等待时间,从0-4秒
  26. //ntnreturns,asanint,anon-negativepseudo-randomnumberin[0,n)fromthedefaultSource.Itpanicsifn<=0.
  27. goroutine(name,time.Duration(rand.Intn(5))*time.Second)
  28. }
  29. //让主进程停住,不然主进程退了,goroutine也就退了
  30. varinputstring
  31. fmt.Scanln(&input)
  32. fmt.Println("done")
  33. }
  34. /*
  35. output:
  36. mba:testgerryyang$./rand_t
  37. 5s
  38. go_00startat2013-12-2813:25:04.460768468+0800HKT
  39. go_01startat2013-12-2813:25:04.460844141+0800HKT
  40. go_02startat2013-12-2813:25:04.460861337+0800HKT
  41. go_02endat2013-12-2813:25:04.460984329+0800HKT
  42. go_02lasted122.992us
  43. go_01endat2013-12-2813:25:05.462003787+0800HKT
  44. go_01lasted1.001159646s
  45. go_00endat2013-12-2813:25:07.461884807+0800HKT
  46. go_00lasted3.001116339s
  47. done
  48. */

关于goroutine是否真正并发的问题,耗子叔叔这里是这样解释的:

引用:

关于goroutine,我试了一下,无论是Windows还是Linux,基本上来说是用操作系统的线程来实现的。不过,goroutine有个特性,也就是说,如果一个goroutine没有被阻塞,那么别的goroutine就不会得到执行。这并不是真正的并发,如果你要真正的并发,你需要在你的main函数的第一行加上下面的这段代码

  1. importruntime
  2. runtime.GOMAXPROCS(n)

本人使用go1.2版本在Linux64,2.6.32内核环境下测试,在上述代码中再添加一个死循环的routine,可以验证上述的逻辑。在没有设置GOMAXPROCS参数时,多个goroutine会出现阻塞的情况;设置GOMAXPROCS参数时,下面的几个routine可以正常执行不会被阻塞。

"time"
  • "runtime"
  • )
  • funcroutine(namestring,delaytime.Duration){
  • t0:=time.Now()
  • fmt.Println(name,t0,",sleep:",delay)
  • //停留xxx秒
  • time.Sleep(delay)
  • t1:=time.Now()
  • fmt.Println(name,t1)
  • //计算时间差
  • fmt.Println(name,t1.Sub(t0))
  • }
  • funcdie_routine(){
  • for{
  • //dieloop
  • }
  • }
  • funcmain(){
  • //实现真正的并发
  • runtime.GOMAXPROCS(4)
  • fmt.Println("setruntime.GOMAXPROCS")
  • fmt.Println(time.Duration(5)*time.Second)
  • //dieroutine
  • godie_routine()
  • varnamestring
  • fori:=0;i<3;i++{
  • name=fmt.Sprintf(//生成ID
  • goroutine(name,time.Duration(rand.Intn(5))*time.Second)
  • }
  • varinputstring
  • fmt.Scanln(&input)
  • fmt.Println("done")
  • }

  • 1 goroutine非并发安全性问题

    这是一个经常出现在教科书里卖票的例子,启了5个goroutine来卖票,卖票的函数sell_tickets很简单,就是随机的sleep一下,然后对全局变量total_tickets作减一操作。

    "time"
  • "math/rand"
  • "runtime"
  • )
  • vartotal_ticketsint32=10
  • funcsell_tickets(iint){
  • for{
  • //如果有票就卖
  • iftotal_tickets>0{
  • time.Sleep(time.Duration(rand.Intn(5))*time.Millisecond)
  • //卖一张票
  • total_tickets--
  • fmt.Println("id:",i,54)">"ticket:",total_tickets)
  • }else{
  • break
  • }
  • }
  • }
  • funcmain(){
  • //设置真正意义上的并发
  • runtime.GOMAXPROCS(4)
  • //生成随机种子
  • rand.Seed(time.Now().Unix())
  • //并发5个goroutine来卖票
  • fori:=0;i<5;i++{
  • gosell_tickets(i)
  • }
  • //等待线程执行完
  • varinputstring
  • fmt.Scanln(&input)
  • //退出时打印还有多少票
  • fmt.Println(total_tickets,54)">"done")
  • }
  • id:1ticket:8
  • id:0ticket:8
  • id:0ticket:7
  • id:2ticket:5
  • id:4ticket:6
  • id:4ticket:3
  • id:3ticket:3
  • id:1ticket:1
  • id:0ticket:2
  • id:3ticket:-1
  • id:2ticket:-1
  • id:1ticket:-2
  • id:4ticket:-3
  • -3done
  • */
  • 上述例子没有考虑并发安全问题,因此需要加一把锁以保证每个routine在售票的时候数据同步。

    "runtime"
  • "sync"
  • )
  • vartotal_ticketsint32=10
  • varmutex=&sync.Mutex{}
  • funcsell_tickets(iint){
  • fortotal_tickets>0{
  • mutex.Lock()
  • //如果有票就卖
  • iftotal_tickets>0{
  • time.Sleep(time.Duration(rand.Intn(5))*time.Millisecond)
  • //卖一张票
  • total_tickets--
  • fmt.Println( }
  • mutex.Unlock()
  • }
  • }
  • funcmain(){
  • id:0ticket:9
  • id:0ticket:6
  • id:0ticket:5
  • id:0ticket:4
  • id:0ticket:3
  • id:0ticket:2
  • id:0ticket:1
  • id:0ticket:0
  • 0done
  • */
  • 2 并发情况下的原子操作问题

    go语言也支持原子操作。关于原子操作可以参考耗子叔叔这篇文章 无锁队列的实现 》,里面说到了一些CAS – CompareAndSwap的操作。下面的程序有10个goroutine,每个会对cnt变量累加20次,所以,最后的cnt应该是200。如果没有atomic的原子操作,那么cnt将有可能得到一个小于200的数。下面使用了atomic操作,所以是安全的。

    "sync/atomic"
  • "time"
  • )
  • funcmain(){
  • varcntuint32=0
  • //启动10个goroutine
  • fori:=0;i<10;i++{
  • gofunc(){
  • //每个goroutine都做20次自增运算
  • fori:=0;i<20;i++{
  • time.Sleep(time.Millisecond)
  • atomic.AddUint32(&cnt,1)
  • }
  • }()
  • }
  • //等待2s,等goroutine完成
  • time.Sleep(time.Second*2)
  • //取最终结果
  • cntFinal:=atomic.LoadUint32(&cnt)
  • fmt.Println("cnt:",cntFinal)
  • }
  • cnt:200
  • */




  • 转帖自http://blog.csdn.net/delphiwcdj/article/details/17630863

    猜你在找的Go相关文章