go中的并发 您所在的位置:网站首页 go异步请求 go中的并发

go中的并发

#go中的并发| 来源: 网络整理| 查看: 265

go中的并发 从并发模型说起

并发目前来看比较主流的就三种:

多线程 每个线程一次处理一个请求,线程越多可并发处理的请求数就越多 在高并发下,多线程的调度开销会比较大。 协程 无需抢占式的调度,开销小,可以有效的提高线程的并发性,从而避免了线程的缺点的部分 基于异步回调的IO模型 利用Linux内核的AIO进行异步IO1 nginx使用的就是epoll模型,通过事件驱动的方式与异步IO回调(伪异步,实际上是程序循环等待IO的出现) goroutine 简介

在go里面,使用goroutine进行并发操作。

goroutine的本质是协程2,也可以认为是轻量级的线程,与创建线程相比,创建成本和开销都很小。同时,Golang 在 runtime、系统调用等多方面对 goroutine 调度进行了封装和处理,当遇到长时间执行或者进行系统调用时,会主动把当前 goroutine 的CPU § 转让出去,让其他 goroutine 能被调度并执行。

Golang 从语言层面支持了协程,从语言层面支持了高并发。

使用3

(这儿不会对语法有过多的说明,大家请去官网查看,官网的教程非常详细,远比其他网页上的资料更全面)

当一个程序启动的时候,只有一个goroutine来调用main函数,称它为主goroutine。新的goroutine通过在函数或者方法前面加上关键字go进行创建。

package main import ( "fmt" "time" ) func Hello() { fmt.Println("Hello") } func main() { go Hello() // 开启一个新的goroutine time.Sleep(1*time.Second) // 大家可以把这个去掉看看会出现什么结果 fmt.Println("World") } /* Hello World */ 特点 终止情况

Go语言中没有任何显示方法可以从外部终止一个 goroutine 的执行。这儿说明一下Goroutine的终止情况。

当Goroutine运行错误,抛出异常,终止程序; 当Goroutine返回,可以通过别的方式进行操控4; 当Main Goroutine结束,所有其他Goroutine强制终止。

之所以这么设计有很多原因。我目前理解地也不是很深刻,简单说一些我认为的原因:

对于杀死的协程占有的资源,需要进行释放与其他管理; 对于杀死的协程占有的锁,需要进行拆除; 禁止之后需要程序员更加注意考虑协程的开始与结束。

栈大小

线程的栈空间是固定分配的,虽然区别于不同的系统会有不同的大小,但是基本都是2MB。这个栈用于保存局部变量,用于在函数切换时使用。

对于goroutine这种轻量级的协程来说,一个大小固定的栈可能会导致资源浪费:比如一个协程里面只print了一个语句,那么栈基本没怎么用。当然,也有可能嵌套调用很深,那么可能也不够用。

所以go采用了动态扩张收缩的策略:初始化为2KB,最大可扩张到1GB。

没有id

每个线程都有一个id,这个在线程创建时就会返回,所以可以很方便的通过id操作某个线程。

但是在goroutine内没有这个概念,这个是go语言设计之初考虑的,防止被滥用,所以你不能在一个协程中杀死另外一个协程,编码时需要考虑到协程什么时候创建,什么时候释放。

GOMAXPROCS

GOMAXPROCS用于设置上下文个数,这个上下文用于协程间的切换,默认值是CPU的个数,也就是说这个个数是指定同时执行协程的内核线程数,即,用户线程(协程)与内核线程的数量对应关系是1:GOMAXPROCS的5(这儿说的1是一个虚指,若有多个协程同时开启,则对应是M:N,如下例子)。

for { go fmt.Print(0) fmt.Print(1) } /* $ GOMAXPROCS=1 go run example.go 11111111111111111100000000000000000000111111111... $ GOMAXPROCS=2 go run example.go 01010101010010101001110010101010010101010101010... */

第一次执行语句指定只启动一个上下文,那么由于是2个协程映射到1个内核线程,那么1次只能跑一个协程,所以会跑一段时间再进行切换(由调度器进行判断什么时候切换,而不是内核)。第二次启动二个上下文,2个协程映射到2个内核线程,那么同一时间有2个干活的内核线程,所以能看到0和1交替打印,也就是说,此时真正实现了并发。

Channel 简介

如果说goroutine是Go并发的执行体,那么 Channel 就是他们之间的连接。

Channel是可以让一个goroutine发送特定的值到另外一个goroutine的通信机制。

在这里插入图片描述

使用 var ch chan int // 声明一个传递int类型的channel ch := make(chan int) // 使用内置函数make()定义一个channel //========= ch done = make(chan string) // 创建一个channel go Hello() fmt.Println( temp := // 定义输出通道类型 time.Sleep(1*time.Second) out echo := make(chan string) receive := make(chan string) go Echo(echo) go Receive(receive, echo) getStr := case case case case case case ch close(ch) }() go func() { DONE: for { time.Sleep(1*time.Second) fmt.Println(time.Now().Unix()) i++ select { case m :=


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有