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()
    
    }
    
  • 相关阅读:
    hdu 4710 Balls Rearrangement()
    hdu 4707 Pet(DFS水过)
    hdu 4706 Children's Day(模拟)
    hdu 4712 Hamming Distance(随机函数暴力)
    csu 1305 Substring (后缀数组)
    csu 1306 Manor(优先队列)
    csu 1312 榜单(模拟题)
    csu 1303 Decimal (数论题)
    网络爬虫
    Python处理微信利器——itchat
  • 原文地址:https://www.cnblogs.com/ailumiyana/p/11107835.html
Copyright © 2011-2022 走看看