21、Goroutines

71 阅读2分钟

本文为译文 goroutines

本文将讨论如何在go中实现并发。

什么是goroutines

goroutines是一个function或者methods可以同时和其他的function或者method同时地运行。goroutines可以被认为是一种轻量的线程,但是创建一个goroutines的代价比创建一个thread代价小很多。因此go应用一般同时会存在上千个goroutines都是很常见的。

goroutines对比thread的优势

  • goroutines的创建对比thread代价极低。一般只需要几kb,且它会随着应用的变化自动伸缩。然而thread的大小是被指定且固定的。
  • goroutines是被多路复用的,其数量比os thread数量更少。可能程序中只有一个thread但是有很多goroutines。如果线程块中的任何goroutines说要等待用户输入,然后就会创建一个新的线程,将剩下的goroutines移进去。所有的这些逻辑都是在运行时做的,我们作为开发者,不需要关注这些,我们只需要用简单的api就可以了。
  • goroutines之间的沟通使用channels做沟通。当使用goroutines访问共享内存时,channel被用来防止race conditions

How to start a Goroutine?

go作为一个method或者function的前缀,你就会得到一个并发运行的goroutines

package main

import (  
    "fmt"
)

func hello() {  
    fmt.Println("Hello world goroutine")
}
func main() {  
    go hello()
    fmt.Println("main function")
}

In line no. 11, go hello() starts a new Goroutine. Now the hello() function will run concurrently along with the main() function. The main function runs in its own Goroutine and it's called the main Goroutine.

This program only outputs the text main function. What happened to the Goroutine we started? We need to understand the two main properties of goroutines to understand why this happens.

  • 当创建一个新的**goroutines****goroutines**的调用会立刻被返回,与函数不同,**control**不会等待**goroutines**执行完成。在**goroutines**之后,**control**立刻回到下一行代码,任何从**goroutines**返回的东西都会被忽略。
  • main goroutines应该保持运行,如果main goroutines被中断了,那么其他的routines也就被中断了。

现在你就能理解为啥上面的程序只输出main function。我们来修改一下代码

package main

import (  
    "fmt"
    "time"
)

func hello() {  
    fmt.Println("Hello world goroutine")
}
func main() {  
    go hello()
    time.Sleep(1 * time.Second)
    fmt.Println("main function")
}

我们用sleep函数去让main goroutines去等待other goroutines执行完成时一种hack行为。真正的写法应该是channelchannel可以被用来阻塞main goroutines直到其他的channel执行完成。我们后边会主要讲channel

Starting multiple Goroutines

package main

import (  
    "fmt"
    "time"
)

func numbers() {  
    for i := 1; i <= 5; i++ {
        time.Sleep(250 * time.Millisecond)
        fmt.Printf("%d ", i)
    }
}
func alphabets() {  
    for i := 'a'; i <= 'e'; i++ {
        time.Sleep(400 * time.Millisecond)
        fmt.Printf("%c ", i)
    }
}
func main() {  
    go numbers()
    go alphabets()
    time.Sleep(3000 * time.Millisecond)
    fmt.Println("main terminated")
}