goroutine协程 | 豆包MarsCode AI刷题

84 阅读2分钟

最近在看goroutine协程有关的知识,顺便做一个笔记记录下来。

goroutine协程

1)进程是程序在操作系统中的一次执行过程,是系统进行进行资源分配和调度的基本单位;

2)线程是进程的一个执行实例,是程序执行的最小单位(比进程更小的能独立运行的基本单位);

3)一个进程可以创建和销毁多个线程,同一个进程中的多个线程可以并发执行;

4)一个程序至少有一个进程,一个进程至少有一个线程;

并行和并发:

1)并发:多线程程序在单核上运行;微观上来看,某一个时间点只有一个任务在执行;

2)并行:多线程程序在多核上运行;微观上来看,某一个时间点有多个任务同时在执行。因此,并行的速度比并发快。

Go协程和Go主线程

1)Go主线程(可以理解为线程/进程):一个Go线程上,可以起多个协程,可以理解为协程是轻量级的线程(编译器做优化);

2)Go协程的特点: 有独立的栈空间; 共享程序堆空间; 调度由用户控制; 协程是轻量级的线程;

如果主线程退出了,则协程即使没有执行完毕, 也会直接退出;协程也可以在主线程没有结束前,完成任务并退出。

3)总结: 主线程是一个物理线程,直接作用在CPU上,是重量级的,非常耗费CPU资源; 协程是从主线程开启的,是轻量级的线程,是逻辑态,对资源的消耗相对小; golang的协程机制是重要的特点,可以轻松开启上万个协程;其他编程语言的并发是基于线程的,开启过多的线程会对资源消耗巨大,这就凸现了golang在并发上的优势;

举例:

package main
 
import (
	"fmt"
	"runtime"
	"strconv"
	"time"
)
 
func test() {
	for i := 1; i < 10; i++ {
		fmt.Println("hello world" + strconv.Itoa(i))
		time.Sleep(time.Second)
	}
}
 
func main() {
	num := runtime.NumCPU()
	fmt.Printf("the number of CPU is %d\n", num)
	runtime.GOMAXPROCS(num) // 设置最大可以调度的CPU的个数
 
	go test() // 开启了一个协程
 
	for i := 1; i < 10; i++ {
		fmt.Println("hello golang" + strconv.Itoa(i))
		time.Sleep(time.Second)
	}
}

goroutine协程存在的问题

  • goroutine协程,来实现并行和并发,如何实现不同协程间的通信;
  • 由于没有对全局变量加锁,故在程序在运行时,可能存在资源竞争的问题(如concurrent map writes)(通过go build -race main.go来检查);
package main
 
import "fmt"
 
var map_ map[int]int = make(map[int]int, 200)
 
func Factorial(n int) {
	var res int
	for i := 1; i < n; i++ {
		res *= i
	}
	map_[n] = res
}
 
func main() {
	for i := 0; i < 200; i++ {
		go Factorial(i)
	}
 
	for index, val := range map_ {
		fmt.Printf("map[%d]=%d\n", index, val)
	}
}