go语言学习笔记 之 协程(goroutine)

特点

  1. go主线程是一个物理线程,直接作用在CPU上,重量级,非常耗费CPU资源
  2. 协程是从主线程开启的,是轻量级的线程,是逻辑态,资源消耗相对较小
  3. go可以轻松开启上万个协程

MPG

  • M: 操作系统的主线程
  • P: 协程执行需要的上下文环境
  • G: 协程
    当多个主线程运行在一个CPU核中,认为该状态为并发
    当多个主线程运行在多个CPU核中,认为该状态为并行

使用

  • 支持使用runtime.GOMAXPROCS方法设置使用的CPU核数
package main

import (
	"fmt"
	"strconv"
	"time"
)

func main() {
	// 启动协程
	go test()
	// 继续主线程
	for i := 1; i <= 10; i++ {
		fmt.Println("main() ..." + strconv.Itoa(i))
		time.Sleep(time.Second)
	}
}

func test() {
	for i := 1; i <= 10; i++ {
		fmt.Println("test() ..." + strconv.Itoa(i))
		time.Sleep(time.Second)
	}
}

结果

main() ...1
test() ...1
test() ...2
main() ...2
test() ...3
main() ...3
main() ...4
test() ...4
test() ...5
main() ...5
main() ...6
test() ...6
test() ...7
main() ...7
test() ...8
main() ...8
main() ...9
test() ...9
main() ...10
test() ...10

资源竞争

多线程计算阶乘

使用锁解决
package main

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

var (
	myMap = make(map[int64]int64, 10)
	// 全局互斥锁
	lock sync.Mutex
)

func Factorial(n int64) {
	var res int64 = 1
	var i int64
	for i = 1; i <= n; i++ {
		res *= i
	}

	lock.Lock()
	myMap[n] = res
	lock.Unlock()
}

func main() {
	for i := 0; i < 20; i++ {
		go Factorial(int64(i))
	}

	time.Sleep(time.Second * 10)

	lock.Lock()
	for k, v := range myMap {
		fmt.Println(k, "=", v)
	}
	lock.Unlock()
}

结果

4 = 24
11 = 39916800
8 = 40320
10 = 3628800
2 = 2
12 = 479001600
13 = 6227020800
0 = 1
5 = 120
3 = 6
7 = 5040
14 = 87178291200
18 = 6402373705728000
1 = 1
6 = 720
9 = 362880
19 = 121645100408832000
17 = 355687428096000
15 = 1307674368000
16 = 20922789888000

使用go build -race main.go查看并发资源竞争

使用channel解决(推荐)

channel本质是一个线程安全的队列,channel是引用类型

管道的使用

// 使用:var 变量名 chan 队列内元素类型
var myChan chan int = make(chan int, 10)
// 向管道写入数据
myChan <- 10
// 从管道读取数据
var num2 int = <- myChan
上一篇:C语言结构体初始化的四种方法


下一篇:达梦数据库之disql体验优化