【go语言学习】并发实现

一、什么是goroutine

Goroutine是Go语言特有的名词。区别于进程Process,线程Thread,协程Coroutine,因为Go语言的创造者们觉得和他们是有所区别的,所以专门创造了Goroutine。

Goroutine是与其他函数或方法同时运行的函数或方法。Goroutines可以被认为是轻量级的线程。与线程相比,创建Goroutine的成本很小,它就是一段代码,一个函数入口。以及在堆上为其分配的一个堆栈(初始大小为4K,会随着程序的执行自动增长删除)。因此它非常廉价,Go应用程序可以并发运行数千个Goroutines。

二、主goroutine

封装main函数的goroutine称为主goroutine。
主goroutine所做的事情并不是执行main函数那么简单。它首先要做的是:设定每一个goroutine所能申请的栈空间的最大尺寸。在32位的计算机系统中此最大尺寸为250MB,而在64位的计算机系统中此尺寸为1GB。如果有某个goroutine的栈空间尺寸大于这个限制,那么运行时系统就会引发一个栈溢出(stack overflow)的运行时恐慌。随后,这个go程序的运行也会终止。

此后,主goroutine会进行一系列的初始化工作,涉及的工作内容大致如下:

  • 创建一个特殊的defer语句,用于在主goroutine退出时做必要的善后处理。因为主goroutine也可能非正常的结束
  • 启动专用于在后台清扫内存垃圾的goroutine,并设置GC可用的标识
  • 执行mian包中的init函数
  • 执行main函数
  • 执行完main函数后,它还会检查主goroutine是否引发了运行时恐慌,并进行必要的处理。最后主goroutine会结束自己以及当前进程的运行。

三、如何使用goroutine

Go语言中使用goroutine非常简单,只需要在调用函数的时候在前面加上go关键字,就可以为一个函数创建一个goroutine。

一个goroutine必定对应一个函数,可以创建多个goroutine去执行相同的函数。

package main

import (
	"fmt"
)

func main() {
	go f1()
	fmt.Println("你好,世界")
}

func f1() {
	fmt.Println("hello world")
}

运行结果

你好,世界

或者

hello world
你好,世界

多次运行以上程序,会出现不同的输出结果。
这是因为:上面的程序有main函数主goroutine,和f1函数子goroutine。程序运行时,当子goroutine优先抢到系统资源,就会输出hello world,然后主goroutine输出你好,世界,结束程序;当主goroutine优先抢到系统资源,就会输出你好,世界,然后主goroutine就结束了,所有在main()函数中启动的goroutine会一同结束。

修改以上代码:

package main

import (
	"fmt"
	"time"
)

func main() {
	go f1()
	fmt.Println("你好,世界")
	time.Sleep(1 * time.Second)
}

func f1() {
	fmt.Println("hello world")
}

运行结果

你好,世界
hello world

增加了主goroutine睡眠1s,这样子goroutine有足够的时间来执行。

四、启动多个goroutine

package main

import (
	"fmt"
	"time"
)

func main() {
	go numbers()
	go alphabets()
	time.Sleep(3000 * time.Millisecond)
	fmt.Println(" main over")
}

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)
	}
}

运行结果

1a23b4c5de main over

原文地址:https://www.cnblogs.com/everydawn/p/13938817.html