go学习笔记(四)——并发

1. goroutine

什么是goroutine

goroutine是golang的最小执行单元,每个go程序至少会有一个主goroutine,这里可以类比成java中额主线程。为了更好的理解goroutine,可以将进程,线程和goroutine(其他语言中的协程)进行类比。

进程,线程和goroutine

进程,线程和协程关系

进程是操作系统进行资源分配,调度和执行的基本单元。当操作系统分配一个进程时,会生成一个唯一对应的PCB块以及为该进程分配专有的系统资源。后续操作系统的调度会通过对应的PCB块来进行。

线程是程序执行流的最小单元,一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成。一般情况下进程与线程为1:n的关系,同一个进程生成的线程会共享改进程的堆上资源。通常情况线程的调度由操作系统内核态进行调度。

协程(goroutine)是更轻量级的线程,或者可以理解为用户态的线程。协程的调度实现答题可以分为两种,一种是用户态的调度实现,比如python中有名的gevent,就是通过用户态调度器实现协程间的调度,这种调度方式存在的一个弊端是由于调度完全是在用户态进行的,所以内核线程与协程的映射关系为1:N,即一个线程内的协程都是交替执行,不能并发;而golang则是在此基础上进行了优化,采用两级调度模型,实现了协程的并发,所以goroutine和内核线程的对应关系为n:m。

1.参考文章2中对于两级调度并发有更进一步的讲解
2.随着这两年互联网的发展,越来越多的语言开始对协程进行了支持,从最开始Python2的greenlet,gevent(线程和协程的对应关系为1:N,用户级线程模型)到Python3在语言层面进行了支持。java中也有kilim和Quasar进行了支持。

2. go关键字

go关键字是golang提供的生成和使用goroutine的关键字,如下代码

  func test(){
    fmt.Println("new goroutine")
  }
  go test()

test函数将在一个新建的goroutine中执行。由于golang在语言层面对于goroutine进行了支持,所以不用想在java中通过实现Runnable接口或者继承Thread后,还要显示的调用start方法才可以在新线程中执行相应的功能。同时由于goroutine的上下文切换开销和所需的内存空间更小(2k),相同性能的机器可以支撑的goroutine数量远远多于可以支持的线程数量。

3. chan关键字

golang将CSP模型作为其并发的基础。正如golang著名的口号一样:"不要以共享内存的方式来通信,相反,要通过通信来共享内存"。既然要用通信的方式来共享内存,所以go就有了channel的出现,来支持已通信的方式共享内存。

    channel = make(chan int ,n)
    channel <- 1 //将信息写入channel
    i := <-channel //从channel中读取信息

对于channel如何为golang的并发保驾护航,将在后面进行详细描述。

4. select关键字

第一次看到select这个关键字的时候,第一反应就是经典的select模型。对想的没错,golang中将通信过程中的通过select进行了语言层面支持了通信层面的多路复用器。

  select{
      case <-ch:
      //TODO
      case xxx:
      //TODO 
  }

可以看到golang中对于select的使用和前文中提到的swtich的关键字使用方法类似。

5. go的内存模型

类似于java中的JMM,go也通过定制内存模型,保证并发的正确性。类比于JMM中的happens-before原则,go中的happens-before原则如下,使用a->b标识a操作happens-before b操作。大体上go的内存模型可以分为以下几类

初始化

  1. 被引入包的init函数优先于本包的所有方法
package p
import q
//q.init -> p.*

2.导入的所有包的init函数->main函数的执行

goroutine相关

  1. goroutine的创建 -> 其执行

  2. goroutine无法确保在程序中的任何事件发生之前退出

func main(){
  go test()
  fmt.Println("mian func")
}

func test(){
  fmt.Println("test")
}
//test不一定能输出

channel管道

5.一个goroutine向一个channel发送数据 ->另一个goroutine从本channel中接收数据

6.当channel执行关闭操作后,channel中已有数据仍可被获取,获取完后,在进行获取则为零值。

var ch = make(chan int ,5)
func main(){

    for i:=0;i<5;i++{
        ch <- i
    }
    close(ch)
    for i:=0;i<10;i++{
        fmt.Println(<-ch)
    }
}
//结果 0 1 2 3 4 0 0 0 0 0

7.无缓存的channel的获取数据会阻塞到向channel中发送数据

var ch = make(chan struct{} ,0)
func main(){
    go func(){
        fmt.Println("step one")
        ch<- struct{}{}
    }()
    <-ch
    fmt.Println("step two")
}

可以用无缓存的channel来实现锁机制

8.对于一个容量为N的channel第k次接收数据 ->该channel的第k+n次发送

保证被消费,channel满了后会阻塞,直到有被消费调的信息,类比java中的BlockingQuene

又见CAS
sync包提供了两种锁 :sync.Mutexsync.WRMutex
sync.Mutex独占锁
sync.WRMutex读写锁
类比Java中相应lock实现,区别go中锁不可重入

9.对于任何 sync.Mutex 或 sync.RWMutex 类型的变量 l 以及 n < m ,对 l.Unlock() 的第 n 次调用在对 l.Lock() 的第 m 次调用返回前发生。

10.对于任何 sync.RWMutex 类型的变量 l 对 l.RLock 的调用,存在一个这样的 n,使得 l.RLock 在对 l.Unlock 的第 n 次调用之后发生(返回),且与其相匹配的 l.RUnlock 在对 l.Lock的第 n+1 次调用之前发生。

Once类型——单例好帮手

11.通过 once.Do(f) 对 f() 的单次调用在对任何其它的 once.Do(f) 调用返回之前发生(返回)。

1.懒汉式在go中的最佳实践,思路同java中的线程安全的懒汉式单例
2.sync 包通过 Once 类型为存在多个Go程的初始化提供了安全的机制。 多个线程可为特定的 f 执行 once.Do(f),但只有一个会运行 f(),而其它调用会一直阻塞,直到 f() 返回。

goroutine和channel随想

1. 示例: 并发的非阻塞缓存
2.java中可以通过ForkJoinPool来实现两级线程模型自定义调度器来实现可并发的java协程?

e.g.1目录文件遍历和空间统计

参考内容:
go内存模型-en
go内存模型-zh
Goroutine并发调度模型深度解析&手撸一个协程池
深入理解golang之channel

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,524评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,869评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,813评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,210评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,085评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,117评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,533评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,219评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,487评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,582评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,362评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,218评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,589评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,899评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,176评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,503评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,707评论 2 335

推荐阅读更多精彩内容