sync包下的互斥锁
场景:当多个协程对共享变量执行写操作的时候就会发生资源竞争,需要用互斥锁。
特点:
互斥锁必须是全局的,即所有协程共用同一把互斥锁
在需要写出执行的代码中对共享变量的写操作加锁。
package main
import (
"fmt"
"sync"
"time"
)
// 需求:现在要计算 1-200 的各个数的阶乘,并且把各个数的阶乘放入到map中。
// 最后显示出来。要求使用goroutine完成
// 思路
// 1. 编写一个函数,来计算各个数的阶乘,并放入到 map中.
// 2. 我们启动的协程多个,统计的将结果放入到 map中
// 3. map 应该做出一个全局的.
var (
myMap = make(map[int]int, 10)
//声明一个全局的互斥锁
//lock 是一个全局的互斥锁,
//sync 是包: synchornized 同步
//Mutex : 是互斥
lock sync.Mutex
)
// test 函数就是计算 n!, 让将这个结果放入到 myMap
func test(n int) {
res := 1
for i := 1; i <= n; i++ {
res *= i
}
//这里我们将 res 放入到myMap
//加锁
lock.Lock()
myMap[n] = res
//解锁
lock.Unlock()
}
func main() {
// 我们这里开启多个协程完成这个任务[20个]
for i := 1; i <= 20; i++ {
go test(i)
}
//休眠10秒钟【第二个问题 】
time.Sleep(time.Second * 5)
//这里我们输出结果,变量这个结果
lock.Lock()
for i, v := range myMap {
fmt.Printf("map[%d]=%d\n", i, v)
}
lock.Unlock()
}
管程channel
管道是一个队列的数据结构,数据遵循了先进先出的原则
管道本身是线程安全的,即多个协程操作同一个管道的时候不需要对管道进行加锁。
管道是有类型的,即一个string类型的管道只能放string类型的数据
管道是引用数据类型,即必须make以后才能使用,make的时候需要指定管道的容量,这个容量是不会改变的,后续最多也只能向这个管道中放入指定容量的数据,多了会deadlock死锁。
管道在close关闭以后就不能向管道中写入数据但依旧可以从管道中读取数据
管道的遍历只能使用for-range遍历
在for-range遍历的时候如果管道没有关闭会出现deadlock死锁问题。
在for-range遍历的时候如果管道关闭了,则正常遍历结束。
因此遍历管道的注意事项: 1、关闭管道 2、 用for-range遍历
type Cat struct {
Name string
Age int
}
func main() {
//定义一个存放任意数据类型的管道 3个数据
var allChan chan interface{}
allChan = make(chan interface{}, 3)
// 向管道中放入数据
allChan <- 10
allChan <- "tom jack"
cat := Cat{"小花猫", 4}
allChan <- cat
//从管道中获取数据
<-allChan
<-allChan
newCat := <-allChan //从管道中取出的Cat是什么?
fmt.Printf("newCat=%T , newCat=%v\n", newCat, newCat)
//使用类型断言,将interface{}类型转为Cat类型
a := newCat.(Cat)
fmt.Printf("newCat.Name=%v", a.Name)
}
func main() {
intChan := make(chan int, 3)
intChan <- 100
intChan <- 200
close(intChan) // close
//这是不能够再写入数到channel
//intChan<- 300
fmt.Println("okook~")
//当管道关闭后,读取数据是可以的
n1 := <-intChan
fmt.Println("n1=", n1)
//遍历管道
intChan2 := make(chan int, 100)
for i := 0; i < 100; i++ {
intChan2 <- i * 2 //放入100个数据到管道
}
//在遍历时,如果channel没有关闭,则会出现deadlock的错误
//在遍历时,如果channel已经关闭,则会正常遍历数据,遍历完后,就会退出遍历
close(intChan2)
for v := range intChan2 {
fmt.Println("v=", v)
}
}
管道实现多协程之间的通信设计
由于主线程推出以后,基于主线程环境创建的协程也会结束,因此必须有一个通信机制使得主线程在其他协程执行的时候阻塞。
//write Data
func writeData(intChan chan int) {
for i := 1; i <= 50; i++ {
//放入数据
intChan <- i //
fmt.Println("writeData ", i)
}
close(intChan) //关闭
}
//read data
func readData(intChan chan int, exitChan chan bool) {
for {
v, ok := <-intChan
if !ok {
break
}
time.Sleep(time.Second)
fmt.Printf("readData 读到数据=%v\n", v)
}
exitChan <- true
close(exitChan)
}
func main() {
//创建两个管道
intChan := make(chan int, 10)
exitChan := make(chan bool, 1)
go writeData(intChan)
go readData(intChan, exitChan)
for {
_, ok := <-exitChan
if !ok {
break
}
}
}