这是我参与「第三届青训营 -后端场」笔记创作活动的的第2篇笔记
进程与线程
线程和进程的关系:每个进程都有相应的线程,在执行程序时,实际上是执行相应的一系列线程。进程是资源分配的最小单位,线程是程序执行的最小单位。
详细讲解:www.php.cn/faq/468521.…
并发与并行
并发:多线程程序在单核上运行
并行:多线程程序在多核上运行
详细讲解:blog.csdn.net/u011555996/…
Go协程和Go主线程
Go主线程:一个Go线程上,可以起多个协程,可以理解为轻量级的线程 测试案例
package main
import (
"fmt"
"strconv"
"time"
)
// 编写一个函数,每隔一秒输出"hello,world"
func test(){
for i := 0; i < 10; i++ {
fmt.Println("hello,world"+strconv.Itoa(i))
time.Sleep(time.Second)
}
}
func main(){
go test() //开启一个协程
for i := 0; i < 10; i++ {
fmt.Println("main hello,golang"+strconv.Itoa(i))
time.Sleep(time.Second)
}
}
执行结果
由输出的效果可知main主线程和test协程同时执行
goroutine的调度模型
MPG模式
M(Machine):操作系统的主线程
P(Processor):协程执行需要的资源(上下文context),可以看作一个局部的调度器,使go代码在一个线程上跑,他是实现从N:1到N:M映射的关键
G(Gorountine):协程,有自己的栈。包含指令指针(instruction pointer)和其它信息(正在等待的channel等等),用于调度。一个P下面可以有多个G
查看,设置golang运行的CPU数
查看逻辑CPU数
cpuNum := runtime.NumCPU()
设置golang运行的CPU数
runtime.GOMAXPROCS(cpuNum)
全局互斥锁解决资源竞争
测试代码
package main
import (
"fmt"
"sync"
"time"
)
var(
myMap = make(map[int]int, 10)
// 声明一个全局的互斥锁
// lock 是一个全局的互斥锁
// sync 是包:synchornized 同步
// Mutex : 是互斥
lock sync.Mutex
)
func test(n int){
res := 1
lock.Lock()
for i := 1; i <= n; i++ {
res *= i
}
lock.Unlock()
// 加锁
lock.Lock()
myMap[n]=res //concurrent map writes
// 解锁
lock.Unlock()
}
func main(){
for i := 1; i <= 20; i++ {
go test(i)
}
time.Sleep(time.Second)
lock.Lock()
for i,v := range myMap {
fmt.Printf("myMap[%v]=%d\n",i,v)
}
lock.Unlock()
}
channel
- channel 本质就是一个数据结构-队列
- 线程安全,多goroutine访问时,不需要加锁
- channel时有类型的,一个string的channel只能存放string类型的数据
channel的基本使用1
代码实现
package main
import(
"fmt"
)
func main(){
// 管道的使用
// 1.创建一个可以存放3个int类型的管道
var intChan chan int = make(chan int,3)
// 2.看看intChan是什么
fmt.Printf("intChan 的值=%v intChan本身的地址=%v\n",intChan,&intChan)
// 3.向管道写入数据
intChan<-10
num := 211
intChan<-num
intChan<-29
// intChan<-90 // 注意点,当我们给管道写入数据时,不能超过容量
// 4.查看管道的长度和cap(容量)
fmt.Printf("channel len=%v,cap=%v\n",len(intChan),cap(intChan)) //3,3
// 5.从管道中读取数据
var num2 int = <-intChan
fmt.Println("num2=",num2)
fmt.Printf("channel len=%v,cap=%v\n",len(intChan),cap(intChan)) //2,3
// 6.在没有使用协程的情况下,如果将管道数据全部取出,再取就会报出 deadlock
num3 := <-intChan
num4 := <-intChan
fmt.Println("num3=",num3,",num4=",num4)
fmt.Printf("channel len=%v,cap=%v\n",len(intChan),cap(intChan)) //0,3
// num5 := <-intChan //deadlock
// fmt.Println(num5)
}
channel的基本使用2
关闭管道: func close(c chan<-Type)
代码实现
package main
import(
"fmt"
)
func main(){
intChan := make(chan int,3)
intChan<-100
intChan<-200
close(intChan) //close
// 这时不能再写入数到channel中
// intChan<-30 //panic: send on closed channel
// 当管道关闭后,读取数据是可以的
n1 := <-intChan
fmt.Println("n1=",n1)
// 遍历管道
intChan2 := make(chan int,100)
for i := 0; i < 100; i++ {
intChan2<-i*2
}
// 在遍历时 如果没有关闭channel,则会出现deadlock的错误
close(intChan2)
for v := range intChan2{
fmt.Println("v=",v)
}
}