Day 12 复习 golang concurrency 语法篇 I

Goroutine

在 go 语言里要使用 goroutine 非常简单,语法如下,不会像 C 语言使用 thread 有太多复杂的语法。(注意:goroutine 很像 thread 但实际上却不是 thread,这里不谈,想看 goroutine 和 thread 的差异已经有很多讨论)。

go func() {
    doSomething()
}()

通常多个 goroutine 会互相沟通,达到正确执行的结果,刚开始学 gorotuine 时会写出像以下的程序,然後什麽都没印出来就结束了,这就是因为 main goroutine 和此 goroutine 没有沟通的结果:在 go spec 指出,要是 main 执行完了是不会等其他 goroutine 的,也就是说在 main goroutine 执行的这段时间里,根本来不及等到 scheduler 排程此匿名 goroutine 并且执行,就结束了。

从这可以观察到:单单只有 goroutine 是不行的,我们需要有工具让多个 goroutine 互相沟通,这工具其实就是 channel。

// Nothing will print
func main() {
    go func() {
        fmt.Println("Hello world")
    }()
}

预期的写法应该如下,其实就是多创造了一个 channel,并且两个不同的 goroutine 做了以下事情

  • 匿名 goroutine:印出 Hello world 之後通知 main goroutine
  • main goroutine:等待匿名 goroutine 的通知
func main() {
    done := make(chan string)
    go func() {
        fmt.Println("Hello world")
        done <- "done"
    }()
    <- done
}

这里只是先给出一个概念,知道需要 goroutine 需要 channel 才会运作的良好。没看懂没关系,之後就看懂了。

Channel

channel 是用来给 goroutine 沟通的。 goroutine 可以对 channel 做两个操作 send 和 receive。
以下就是建立一个 channel 的语法。 string 型态代表,此 channel 所 send 和 receive 的是一个 string。

ch := make(chan string)

send 操作,将某个值放入 channel

ch <- "hello"

receive 操作,使用某个变数接收 channel 里的值 (line 1),也可以接收到之後把这个值丢掉 (line 2)

myVar := <- ch
<- ch

接下来我们将会谈到,channel 的 block 机制

Unbuffered channel

以下我将会使用几个名词:传送者就是只使用 send 的人,接收者是指使用 receive 操作的人

我们可以把 unbuffered channel 想像成一个挂号信的传送,信件一定要传输到收信人手上,我们分别用传送者跟接收者的角度来看:
传送者:目的把信件送到接收者手上,等到接收者出现我才去做其他事情
接收者:目的在从传送者手上收到信件,等到接收者出现我才去做其他事情

我们刚刚所讲的语法其实就是 Unbuffered channel,再看一次:

ch := make(chan string)

Buffered Channel

把 Buffered Channel 想像成一种特殊的平信,信件一定要塞到信箱里,我们分别用传送者跟接收者的角度来看:
传送者:目的把信件送到信箱里,如果信箱满了,那就等,等到信有位置了再把信塞进去
接收者:目的在把信件从信箱拿出来,如果信箱空了,那就等,等到有信了再从信箱拿出来

以下就是 buffer 大小为五个 string 的 channel,如果在 channel 没有人接收的情况下,在 send 一个 string 则会 block

ch := make(chan string, 5)

关闭 channel

我们可以透过 close 语法关闭 channel

ch := make(chan string)
close(ch)

注意:

  • 对於关闭的 channel 做 send 操错会 runtime panic
  • 关闭已经关闭的 channel 换 runtime panic
  • 对於已关闭的 channel ,做出 receive 操作得话会先接收到 close 之前 send 出的值,在之後接收的值就是 channel 型态的 zero value。可以看下面的例子。
func main() {
	ch := make(chan int, 2)
	go func() {
		ch <- 1
		ch <- 2
		close(ch)
	}()
	fmt.Println(<-ch) // print 1
	fmt.Println(<-ch) // print 2
	fmt.Println(<-ch) // print 0
}

Reference: https://golang.org/ref/spec#Close

Reference


<<:  走骇客的路让骇客无路可走

>>:  Swift纯Code之旅 Day2. 「谁是主画面?」

[Aras笔记] 从Excel快速贴上受影响物件并建立变更单

###本文章内容皆由我本人开发撰写与分享 在变更单建立时,经常会花时间编辑受影响物件 经常是为了从E...

Day12:终於要进去新手村了-Javascript-资料型态转换-将字串变成数字

前两篇文章中有认识到了变数是要用来放资料的,但是有时候会遇到资料内容需要不同的类型,比如说数字,它可...

vok-orm 关联性资料的新增/查询 (上篇) -- d08

本节重点 延续先前己建立的学生资料范例,今天加上学生成绩。 建立成绩资料 在显示单笔学生资料页Stu...

[Day 12] Forensics 小挑战

今天心情蛮好的,期待叻2周终於等到这天了,生平第一次染发:) 这篇文有一半是我边染发变打出来的~ 染...

[Day06] 团队系统设计 - 张力分析

某次参加站立会议,某位 PM 情绪激动的说:「我後天要跟客户回报进度了,但 A 同事这周请假三天,明...