zoukankan      html  css  js  c++  java
  • golang协程同步的几种方法

    golang协程同步的几种方法


    本文简要介绍下go中协程的几种同步方法。

    协程概念简要理解

    协程类似线程,是一种更为轻量级的调度单位,但协程还是不同于线程的,线程是系统级实现的,常见的调度方法是时间片轮转法,如每隔10ms切换一个线程执行。

    协程则是应用软件级实现,它和线程的原理差不多,当一个协程调度到另一个协程时,将上一个协程的上下文信息压入堆栈,来回切换。一个线程可以跑很多个协程,由这个线程来调度协程的切换,如果是C/C++的话底层就能通过select/poll/epoll来做,例如微信后台的开源libco库。

    golang协程底层不是C,纯go实现,golang的协程应该是目前各类有协程概念的语言中实现的最完整和成熟的,调度是基于GPM模型实现的,有兴趣可以去了解下,这里不扯远了,下面看看协程的同步。

    为什么要做同步

    至于为什么需要同步呢,类似线程要做同步差不多,现在的cpu都是多核,假设一核一个线程同时一起访问同一块内存中的数据吗,那么可能上一ns第一个线程刚把数据从寄存器拷贝到内存,第二个线程马上又把此数据用它修改的值给覆盖了,这样共享数据变会乱套。

    举个例子 :

    用2个协程序并发各自增一个全局变量100 0000 次

    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的结果.
    Alt text

    协程的几种同步方法

    Mutex

    互斥锁,可以创建为其他结构体的字段;零值为解锁状态。Mutex类型的锁和线程无关,可以由不同的线程加锁和解锁。

    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)
    
    }
    

    Alt text

    channel

    使用golang的channel, 下面一个典型的生产消费模型

    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)
    }
    

    Alt text

    WaitGroup

    sync包中的WaitGroup可用等待一组协程的结束。
    父协程通过Add方法来设定应等待的线程的数量。
    每个被等待的协程在结束时调用Done方法。
    同时,主协程里调用Wait方法阻塞至所有线程结束。

    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()
    
    }
    
  • 相关阅读:
    [extjs] ExtJs4.2 Form 表单提交
    [java ] java.util.zip.ZipException: error in opening zip file
    Oracle 11g 执行计划管理1
    Oracle 分区表的统计信息实例
    Oracle 手动收集统计信息
    Oracle 10g 之自动收集统计信息
    Oracle 11g 之自动收集统计信息
    Oracle 11gR2 RAC修改SCAN IP
    共享内存shared pool (6):追踪sql语句
    共享内存shared pool (5):详解一条SQL在library cache中解析
  • 原文地址:https://www.cnblogs.com/ailumiyana/p/11107835.html
Copyright © 2011-2022 走看看