Go-goroutine

Goroutine基本介绍

进程和线程说明:

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

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

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

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

并发和并行:

(1)多线程程序在单核上运行就是并发。

(2)多线程程序在多核上运行就是并行。

Go协程和Go主线程:

(1)Go主线程:一个Go线程上可以起多个协程,协程是轻量级线程;

(2)Go协程的特点:

  • 有独立的栈空间
  • 共享程序堆空间
  • 调度由用户控制
  • 协程是轻量级线程

Go实例

简单实例:

package main

import (
	"fmt"
	"time"
)

func test() {
	
	for i := 0; i < 10; i++ {
		fmt.Println("test", "Hello world", i)
		time.Sleep(time.Second)
	}
}

func main() {

	// 开启goroutine
	go test()

	for i := 0; i < 10; i++ {
		fmt.Println("main", "Hello golang", i)
		time.Sleep(time.Second)
	}
}

设置CPU使用个数:

package main

import (
	"fmt"
	"runtime"
)

func main() {

	// CPU个数
	num := runtime.NumCPU()
	// 设置使用的CPU个数
	runtime.GOMAXPROCS(num)
	fmt.Println(num)
}

资源竞争问题:

在运行某程序时,如何知道是否存在资源竞争问题?

go build -race test.go

资源竞争问题:

package main

import (
	"fmt"
	"time"
)

/*
需求:计算1-200各个数的阶乘,并且把各个数的阶乘放到map中。最后显示出来。
问题:会出现并发/并行安全问题(Found 2 data race(s))
*/

var (
	myMap = make(map[int]int, 10)
)

// 计算n的阶乘,将结果放入到map中
func test(n int) {
	res := 1
	for i := 1; i <= n; i++ {
		res *= i
	}

	// fatal error: concurrent map writes
	myMap[n] = res
}

func main() {

	for i := 1; i <= 200; i++ {
		go test(i)
	}

	// 设置休眠时间要多长?
	time.Sleep(time.Second * 10)

	for i, v := range myMap {
		fmt.Printf("map[%d]=%d\n", i, v)
	}
}

解决方法:使用互斥锁mutex

package main

import (
	"fmt"
	"time"
	"sync"
)

/*
需求:计算1-200各个数的阶乘,并且把各个数的阶乘放到map中。最后显示出来。
问题:会出现并发/并行安全问题(Found 2 data race(s))
解决方法:使用互斥锁,因为阶乘容易越界,所以改成sum += uint64(i)
*/

var (
	myMap = make(map[int]int, 10)
	// 定义一个全局互斥锁
	lock sync.Mutex
)

// 计算n的阶乘,将结果放入到map中
func test(n int) {
	res := 0
	for i := 1; i <= n; i++ {
		res += i
	}

	// 加锁
	lock.Lock()
	myMap[n] = res
	// 解锁
	lock.Unlock()
}

func main() {

	for i := 1; i <= 200; i++ {
		go test(i)
	}

	// 设置休眠时间要多长?
	time.Sleep(time.Second * 10)

	// 加互斥锁的原因:
	// 按理来说10s内上述的协程都应该执行完了,后面就不会出现资源竞争的问题
	// 但是实际上还是有可能出现竞争问题
	// 我们的程序从设计上可以知道10s内执行完所有协程,但是主线程并不知道
	// 因此底层还是可能出现资源争夺,所以还是需要加入互斥锁

	// 加锁
	lock.Lock()
	for i, v := range myMap {
		fmt.Printf("map[%d]=%d\n", i, v)
	}
	// 解锁
	lock.Unlock()
}

 

参考资料大全

Go资料大全:https://github.com/0voice/Introduction-to-Golang

Go中文网链接:https://studygolang.com/

Golang标准文档:https://studygolang.com/pkgdoc

posted on 2021-08-18 13:57  温昀  阅读(33)  评论(0编辑  收藏  举报

导航