1.多线程
- 1.线程是由操作系统进行管理,也就是处于内核态
- 2.线程之间进行切换,需要发生用户态到内核态的切换
- 3.当系统中运行大量线程,系统就会变得非常慢
- 4.用户态的线程,支持大量线程创建,也叫协程或
goroutine
2.创建goroutine
1.使用go这个关键字创建一个新的轻量级线程(也叫协程)
package main
import "fmt"
func hello() {
fmt.Println("hello world")
}
func main() {
// 开启新的线程去执行hello()
go hello()
fmt.Println("This is a test")
}
- 在
go语言中,主线程退出后,子线程不会继续执行,也就是没有守护线程
2.Goroutine原理浅析
- 一个操作系统线程对应用户态多个
goroutine
- 同时使用多个操做系统线程
- 操做系统线程对
goroutine是多对多关系,即M:N
![]()
![]()
![]()
3.多核控制
1.通过runtime包进行多核设置
2.GOMAXPROCS设置当前程序运行时占用的cpu核数
3.NumCPU获取当前系统的CPU核数
package main
import (
"fmt"
"runtime"
"time"
)
var j int
func hello() {
for {
j++
}
}
func main() {
cpu := runtime.NumCPU()
fmt.Println("cup num: ", cpu) // 获取cpu物理核数
runtime.GOMAXPROCS() // 当前程序不管开启多少个协程,只占用一核的CPU
for i := 0; i < 10; i++ {
go hello()
}
time.Sleep(time.Hour)
}
![]()
4.channel介绍
1.概念
- 1.本质上就是一个队列,是一个容器
- 2.因此定义的时候,需要指定容器中元素的类型
- 3.示例:
var nums chan int
2.操做
// 声明一个整形队列,c此时是一个nil对象
var c chan int
// 初始化,容量给1000
c = make(chan int, 1000)
// 入队
c <- 100
// 出队(可以使用for range)
data := <- c
fmt.Println(len(c))
fmt.Println(cap(c))
3.实例
package main
import "fmt"
func main() {
// 声明一个整形队列,c此时是一个nil对象
var c chan int
// 初始化,容量给1000
c = make(chan int, 3)
c <- 10
c <- 9
c <- 8
data := <- c
fmt.Println(data)
}
4.阻塞chan
package main
import (
"fmt"
"time"
)
func test(flag chan bool) {
for i := 0; i < 10; i++ {
fmt.Println(i)
time.Sleep(time.Second)
}
flag <- true
}
func main() {
var flag chan bool
flag = make(chan bool)
go test(flag)
<-flag
}
4.提示
- 1.如果初始化空队列,即不指定容量,此时无法直接插入元素,只有当有程序从队列往外取值时,才可以插入
- 2.如果队列中没有元素,取值时会阻塞等待,直到取到元素后继续往下执行
5.单向channel介绍
1.只写队列
chan<- int
2.只读队列
<- chan
3.关闭队列
close(xxx)
4.判断队列是否关闭
data, ok := <- chan
if !ok {
break
}
5.队列遍历
for v := range nums1 {
println(v)
}
6.实例
package main
import (
"fmt"
)
func main() {
var nums1 <-chan int
nums1 = make(<- chan int, 10)
//nums1 <- 1 // Invalid operation: nums1 <- 1 (send to receive-only type <-chan int)
data , ok := <- nums1
if !ok {
fmt.Println("队列已关闭")
}
fmt.Println(data)
}
6.Waitgroup
1.如何等待一组groutine结束
package main
import (
"fmt"
"time"
)
func process(i int, flag chan bool) {
fmt.Println("start goroutine: ", i)
time.Sleep(time.Second)
fmt.Printf("goroutine %d ended\n", i)
flag <- true
}
func main() {
flag := make(chan bool, 3)
for i := 0; i < 3; i++ {
go process(i, flag)
}
for j := 0; j < 3; j++ {
<- flag
}
fmt.Println("All go routines finished executing")
}
/*
start goroutine: 2
start goroutine: 1
start goroutine: 0
goroutine 1 ended
goroutine 0 ended
goroutine 2 ended
All go routines finished executing
*/
package main
import (
"fmt"
"sync"
"time"
)
func process(i int, wg *sync.WaitGroup) {
fmt.Println("start goroutine: ", i)
time.Sleep(time.Second)
fmt.Printf("goroutine %d ended\n", i)
wg.Done() // 计数-1
}
func main() {
var wg sync.WaitGroup
for i := 0; i < 3; i++ {
wg.Add(1) // 计数+1
go process(i, &wg)
}
wg.Wait() // 计数=0时结束
fmt.Println("All go routines finished executing")
}
7.Workerpool的实现
1.worker池的实现
- 1.生产者、消费者模型、简单有效
- 2.控制
goroutine的数量,防止goroutine泄露和暴涨
- 3.基于
goroutine和chan,构建workerpool非常简单
2.项目需求分析
- 计算一个数字的各个位数之和,例如:123,和等于
1+2+3=6
- 需要计算的数字使用随机算法生成
3.方案介绍
![]()
4.编码实现
package main
import (
"fmt"
"math/rand"
)
type Job struct {
Number int
Id int
}
type Result struct {
job Job
sum int
}
func Add(job *Job, result chan *Result) {
sum := 0
number := job.Number
for number != 0{
tmp := number % 10 // 对10取余,得到个位数
sum += tmp
number /= 10 // 除10取整,抛弃个位数
}
r := &Result{
job: *job,
sum: sum,
}
result <- r
}
func Worker(jobChan chan *Job, resultChan chan *Result) {
for job := range jobChan {
Add(job, resultChan)
}
}
func startWorker(num int, jobChan chan *Job, resultChan chan *Result) {
for i := 0; i < num; i++ {
go Worker(jobChan, resultChan)
}
}
func printResult(resultChan chan *Result) {
for result := range resultChan {
fmt.Printf("job id: %d number: %v result: %d\n", result.job.Id, result.job.Number, result.sum)
}
}
func main() {
jobChan := make(chan *Job, 1000)
resultChan := make(chan *Result, 1000)
// worker池创建,池中每个worker都从job队列中取数据,然后取计算,计算结果插入result结果队列
startWorker(128, jobChan, resultChan)
// 单独线程从结果池中取
go printResult(resultChan)
// 主线程生产job任务
id := 1
for {
// 生成随机数
number := rand.Int()
job := &Job{
Id: id,
Number: number,
}
jobChan <- job
id += 1
}
}