[关闭]
@adamhand 2019-02-05T13:24:33.000000Z 字数 1413 阅读 652

golang--并发


Go 对并发的支持

Go 编程语言原生支持并发。Go 使用 Go 协程(Goroutine)信道(Channel)来处理并发。

Go 协程是什么?

Go 协程是与其他函数或方法一起并发运行的函数或方法。Go 协程可以看作是轻量级线程。与线程相比,创建一个 Go 协程的成本很小。因此在 Go 应用中,常常会看到有数以千计的 Go 协程并发地运行。

Go 协程相比于线程的优势

相比线程而言,Go 协程的成本极低。堆栈大小只有若干 kb,并且可以根据应用的需求进行增减。而线程必须指定堆栈的大小,其堆栈是固定不变的。
Go 协程会复用(Multiplex)数量更少的 OS 线程。即使程序有数以千计的 Go 协程,也可能只有一个线程。如果该线程中的某一 Go 协程发生了阻塞(比如说等待用户输入),那么系统会再创建一个 OS 线程,并把其余 Go 协程都移动到这个新的 OS 线程。所有这一切都在运行时进行,作为程序员,我们没有直接面临这些复杂的细节,而是有一个简洁的 API 来处理并发。
Go 协程使用信道(Channel)来进行通信。信道用于防止多个协程访问共享内存时发生竞态条件(Race Condition)。信道可以看作是 Go 协程之间通信的管道。我们会在下一教程详细讨论信道。

如何启动一个 Go 协程?

调用函数或者方法时,在前面加上关键字 go,可以让一个新的 Go 协程并发地运行。

  1. package main
  2. import (
  3. "fmt"
  4. )
  5. func hello() {
  6. fmt.Println("Hello world goroutine")
  7. }
  8. func main() {
  9. go hello()
  10. fmt.Println("main function")
  11. }

运行一下程序,你会很惊讶!

该程序只会输出文本 main function。我们启动的 Go 协程究竟出现了什么问题?要理解这一切,我们需要理解两个 Go 协程的主要性质。

我们现在修复这个问题。

  1. package main
  2. import (
  3. "fmt"
  4. "time"
  5. )
  6. func hello() {
  7. fmt.Println("Hello world goroutine")
  8. }
  9. func main() {
  10. go hello()
  11. time.Sleep(1 * time.Second)
  12. fmt.Println("main function")
  13. }

启动多个 Go 协程

  1. package main
  2. import (
  3. "fmt"
  4. "time"
  5. )
  6. func numbers() {
  7. for i := 1; i <= 5; i++ {
  8. time.Sleep(250 * time.Millisecond)
  9. fmt.Printf("%d ", i)
  10. }
  11. }
  12. func alphabets() {
  13. for i := 'a'; i <= 'e'; i++ {
  14. time.Sleep(400 * time.Millisecond)
  15. fmt.Printf("%c ", i)
  16. }
  17. }
  18. func main() {
  19. go numbers()
  20. go alphabets()
  21. time.Sleep(3000 * time.Millisecond)
  22. fmt.Println("main terminated")
  23. }

该程序会输出:

  1. 1 a 2 3 b 4 c 5 d e main terminated

程序的运作如下图所示。



添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注