golang中的互斥锁和管道

简介: golang中的互斥锁和管道

公众号merlinsea


  • 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)
   }
}


  • 管道实现多协程之间的通信设计
  • 由于主线程推出以后,基于主线程环境创建的协程也会结束,因此必须有一个通信机制使得主线程在其他协程执行的时候阻塞。


640.jpg


//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
      }
   }
}




相关文章
|
3月前
|
Go
Golang 中的互斥锁是什么?
# go # programming # beginners # architecture
18 0
|
3月前
|
存储 安全 Go
Golang分段锁
Golang分段锁
31 0
|
3月前
|
存储 Go
浅谈Golang互斥锁sync.Mutex
浅谈Golang互斥锁sync.Mutex
13 0
|
3月前
|
存储 编译器 Go
Golang底层原理剖析之互斥锁sync.Mutex
Golang底层原理剖析之互斥锁sync.Mutex
30 0
|
6月前
|
Go
Golang 语言标准库 sync 包的 RWMutex 读写互斥锁怎么使用?
Golang 语言标准库 sync 包的 RWMutex 读写互斥锁怎么使用?
27 0
|
6月前
|
存储 Go
No.13 golang中channel(管道)常见使用场景?(下)
No.13 golang中channel(管道)常见使用场景?
|
6月前
|
存储 Go
No.13 golang中channel(管道)常见使用场景?(上)
No.13 golang中channel(管道)常见使用场景?
101 0
|
9月前
|
存储 Go 调度
golang 锁原理剖析,你值得收藏
golang 锁原理剖析,你值得收藏
|
11月前
|
存储 Go
Golang中互斥锁和读写互斥锁
Golang中互斥锁和读写互斥锁
69 0
|
11月前
|
存储 Go
Golang中的管道(channel) 、goroutine与channel实现并发、单向管道、select多路复用以及goroutine panic处理
Golang中的管道(channel) 、goroutine与channel实现并发、单向管道、select多路复用以及goroutine panic处理
396 0