Go-协程(Goroutine)

Goroutine是什么?

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

Goroutine相对于线程的优势

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

如何启动一个Goroutine?

Go启动协程非常简单,只需要在调用函数或方法时,在前面加上关键字 go ,既可以让一个新的Go协程并发地运行。

package main

import(
    "fmt"
    "time"
)

func calc() {
    fmt.Println("hello")
    time.Sleep(3 * time.Second)
    fmt.Println("hello end") // 这一行为什么不会打印?因为主协程已经执行完毕了。
}


func main() {
    // 如果协程还没创建,主协程就结束了,协程也无法运行。
    go calc()
    time.Sleep(1 * time.Second)  // 这里睡眠一秒让协程有时间运行起来
    fmt.Println("main function")
}
View Code
  • 启动一个新的协程时,协程调用会立即返回。与函数不同,程序控制不会去等待Go协程执行完毕。在调用Go协程之后,程序控制会立即执行到代码的下一行,而忽略该协程的任何返回值。
  • 如果希望运行其它Go协程,Go主协程必须继续运行着。如果Go主协程终止,其它Go协程也不会继续运行下去,即使已经开始运行。

启动多个Goroutine

package main

import (  
    "fmt"
    "time"
)

func numbers() {  
    for i := 1; i <= 5; i++ {
        time.Sleep(250 * time.Millisecond) // 1250
        fmt.Printf("%d ", i)
    }
}
func alphabets() {  
    for i := 'a'; i <= 'e'; i++ {
        time.Sleep(400 * time.Millisecond) // 2000
        fmt.Printf("%c ", i)
    }
}
func main() {  
    go numbers()
    go alphabets()
    time.Sleep(2100 * time.Millisecond)  //主协程总运行时间必须大于某个子协程的总执行时间。
    fmt.Println("main terminated")
}
View Code

Goroutine其它操作

线程是由操作系统进行管理的处于内核态,线程之间进行切换,会发生用户态到内核态的切换。当系统中运行大量线程时,系统就会频繁切换,由此系统运行就会变慢、而用户态的线程,支持大量线程创建,也叫协程或者Goroutine。

package main

import (
	"fmt"
	"runtime"
	"time"
)
func calc() {
	var i int
	for {
		i++
	}
}
func main() {
   // 多核控制 cpu := runtime.NumCPU() // 查看cpu核数 fmt.Println("cpu: ", cpu) runtime.GOMAXPROCS(cpu) // 代码使用cpu核数 /* 默认使用所有cpu */ for i := 0; i < 10; i++ { go calc() } time.Sleep(time.Hour) }

 

posted @ 2020-05-06 22:38  TrumpC  阅读(179)  评论(0)    收藏  举报