理解Golang的goroutine和channel

烂笔头啊烂笔头......

golang是最好的并发语言,拒绝任何反驳。

说到golang的并发,必须要提一个概念--协程

开销:进程>线程>协程 三者之间的关系可自行百度

目前并发比较流行的三种模式:

1、多线程:每个线程一次处理一个请求,线程越多可并发处理的请求数就越多,但是在高并发下,多线程开销会比较大。

2、协程:无需抢占式的调度,开销小,可以有效的提高线程的并发性,从而避免了线程的缺点的部分。

3、基于异步回调的IO模式,说个比较流行的快速建站语言--nodejs,通过事件驱动的方式与异步IO回调,使得服务器持续运转,来支撑高并发的请求

说正题.....

goroutine

goroutine就是golang中的协程,也可以叫做轻量级的现成,与线程比较,创建的成本以及开销都小很多,每个goroutine的堆栈只有几kb,并且堆栈可以根据程序的需要增长和缩小,而线程的堆栈是需要指明和固定的。所以golang从语言层面上支持高并发。

创建格式 go func()

goroutine正确的使用姿势一:主协成结束时,其他协程都会强制结束!

package main

import (
    "fmt"
    "time"
)

func out() {
    fmt.Println("我想先出来")
}
func main() {
    go out()
    time.Sleep(1 * time.Second)
    fmt.Println("好吧,看在sleep哥的份上,让你先出来")
}

执行结果:

我想先出来
好吧,看在sleep哥的份上,让你先出来

如果把time.Sleep注释了,main就不会再等其他协程执行完了,你可能就看不到out的输出了。

goroutine正确的使用姿势二:不同的goroutine之间不会相互影响

package main

import (
    "fmt"
    "strconv"
    "time"
)

func PrintMany() {
    for i := 1; i <= 3; i++ {
        time.Sleep(250 * time.Millisecond)
        fmt.Println("打印第" + strconv.Itoa(i) + "个")
    }
}
func PrintSingle() {
    fmt.Println("我就打印一个")
}
func main() {
    go PrintMany()
    go PrintSingle()
    time.Sleep(2 * time.Second)
    fmt.Println("我是最后一个")
}

执行结果:

我就打印一个
打印第1个
打印第2个
打印第3个
我是最后一个

PrintMany里面有Sleep,是否会导致第二个goroutine阻塞或者等待呢?no。因为两个goroutine之间不会相互影响,main会继续按顺序执行下去

goroutine伴侣--channel

回到姿势一中的例子,把Sleep注释后,你可能就看不见go out()的输出了。因为main非常之霸道,这时候就需要channel去阻止它。

package main

import (
    "fmt"
)

var bol chan bool

func One() {
    fmt.Println("I am One")
    bol <- true
}
func main() {
    bol = make(chan bool)
    go One()
    <-bol
    fmt.Println("我又要最后一个出来了")
}

执行结果:

I am One
我又要最后一个出来了

<-bol 能起到了阻塞作用。

对channe阻塞的理解:
发送者角度:对于同一个通道,发送操作(协程或者函数中的),在接收者准备好之前是阻塞的。如果chan中的数据无人接收,就无法再给通道传入其他数据。因为新的输入无法在通道非空的情况下传入。所以发送操作会等待 chan 再次变为可用状态:就是通道值被接收时(可以传入变量)。

package main

import (
    "fmt"
)

var bol chan bool

func One() {
    fmt.Println("I am One")
    bol <- true
}
func main() {
    bol = make(chan bool)
    go One()
    bol <- false //bol中的数据还未取出,无法再传入数据,所以会报错:deadlock!
    fmt.Println("我又要最后一个出来了")
}

接收者角度:对于同一个通道,接收操作是阻塞的(协程或函数中的),直到发送者可用:如果通道中没有数据,接收者就阻塞了。

package main

import (
    "fmt"
)

var bol chan bool

func One() {
    fmt.Println("I am One")
}
func main() {
    bol = make(chan bool)
    go One()
    <- bol //deadlock!
    fmt.Println("我又要最后一个出来了")
}

Channel类型的三种定义格式:

chan int //既可以发送数据也可以接受数据
<- chan int  //只允许从chan接受int类型数据
chan <- int //只允许发送int类型数据到chan

Channel默认是阻塞的。如果要“缓解”阻塞,就要给channel加一个缓冲区。

ch := make(chan string, 3) // 创建了缓冲区为3的通道

//=========
len(ch)   // 长度计算  计算缓冲区已填充的长度
cap(ch)   // 容量计算  计算缓冲区的长度

select关键字用法

select和常用的switch用法类似,但select只用来监听和channel有关的IO操作,当IO操作发生时,触发相应的动作。select的case机会均等,既如果多个case都满足运行条件,则select会随机选出一个执行,其他不会执行。没有default的select有几率发生死锁事件。

select的应用场景
1、实现timeout机制

package main

import (
    "fmt"
    "time"
)

func main() {
    timeout := make (chan bool, 1)
    go func() {
        time.Sleep(1*time.Second) // 休眠1s,如果超过1s还没I操作则认为超时,通知select已经超时啦~
        timeout <- true
    }()
    ch := make (chan int)
    select {
    case <- ch:
    case <- timeout:
        fmt.Println("超时啦!")
    }
}
//====================
package main

import (
    "fmt"
    "time"
)

func main() {
    ch := make (chan int)
    select {
    case <-ch:
    case <-time.After(time.Second * 1): // 利用time来实现,After代表多少时间后执行输出东西;After返回一个Time类型的只读channel
        fmt.Println("超时啦!")
    }
}

2.判断channel是否阻塞(或者说channel是否已经满了)

package main

import (
    "fmt"
)

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