golang协程同步的几种方法

栏目: Go · 发布时间: 5年前

内容简介:本文简要介绍下go中协程的几种同步方法。协程类似线程,是一种更为轻量级的调度单位,但协程还是不同于线程的,线程是系统级实现的,常见的调度方法是时间片轮转法,如每隔10ms切换一个线程执行。协程则是应用软件级实现,它和线程的原理差不多,当一个协程调度到另一个协程时,将上一个协程的上下文信息压入堆栈,来回切换。一个线程可以跑很多个协程,由这个线程来调度协程的切换,如果是C/C++的话底层就能通过select/poll/epoll来做,例如微信后台的开源libco库。

本文简要介绍下 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的结果.

golang协程同步的几种方法

协程的几种同步方法

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)

}

golang协程同步的几种方法

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

golang协程同步的几种方法

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

}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

解放战争(上)(1945年8月—1948年9月)

解放战争(上)(1945年8月—1948年9月)

王树增 / 人民文学出版社 / 2009-8 / 60.00

《解放战争》为王树增非虚构文学著述中规模最大的作品。武器简陋、兵力不足的军队对抗拥有现代武器装备的兵力庞大的军队,数量不多、面积有限的解放区最终扩展成为九百六十万平方公里的共和国,解放战争在短短四年时间里演绎的是人类历史上的战争传奇。国际风云,政治智慧,时事洞察,军事谋略,军队意志,作战才能,作品具有宏阔的视角和入微的体察,包含着惊心动魄的人生沉浮和变幻莫测的战场胜负,尽展中国历史上规模最大的一场......一起来看看 《解放战争(上)(1945年8月—1948年9月)》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

MD5 加密
MD5 加密

MD5 加密工具

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具