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




相关文章
|
7月前
|
安全 Go
Golang深入浅出之-互斥锁(sync.Mutex)与读写锁(sync.RWMutex)
【4月更文挑战第23天】Go语言并发编程中,`sync.Mutex`和`sync.RWMutex`是保证线程安全的关键。互斥锁确保单个goroutine访问资源,而读写锁允许多个读者并发访问。常见问题包括忘记解锁、重复解锁以及混淆锁类型。使用`defer`可确保解锁,读写锁不支持直接升级或降级,需释放后再获取。根据读写模式选择合适锁以避免性能下降和竞态条件。理解并正确使用锁是编写并发安全程序的基础。
152 3
|
3月前
|
Go
Golang语言之管道channel快速入门篇
这篇文章是关于Go语言中管道(channel)的快速入门教程,涵盖了管道的基本使用、有缓冲和无缓冲管道的区别、管道的关闭、遍历、协程和管道的协同工作、单向通道的使用以及select多路复用的详细案例和解释。
140 4
Golang语言之管道channel快速入门篇
|
3月前
|
安全 Go
Golang语言goroutine协程并发安全及锁机制
这篇文章是关于Go语言中多协程操作同一数据问题、互斥锁Mutex和读写互斥锁RWMutex的详细介绍及使用案例,涵盖了如何使用这些同步原语来解决并发访问共享资源时的数据安全问题。
100 4
|
2月前
|
中间件 Go 数据处理
应用golang的管道-过滤器架构风格
【10月更文挑战第1天】本文介绍了一种面向数据流的软件架构设计模式——管道-过滤器(Pipe and Filter),并通过Go语言的Gin框架实现了一个Web应用示例。该模式通过将数据处理流程分解为一系列独立的组件(过滤器),并利用管道连接这些组件,实现了模块化、可扩展性和高效的分布式处理。文中详细讲解了Gin框架的基本使用、中间件的应用以及性能优化方法,展示了如何构建高性能的Web服务。
84 0
|
4月前
|
SQL 安全 Java
golang为什么不支持可重入锁?
本文对比分析了Java与Go语言中锁机制的不同。在Java中,无论是`synchronized`关键字还是`ReentrantLock`都支持可重入特性,通过维护一个计数器来跟踪锁的嵌套级别,确保同一线程可以多次获取同一把锁而不会造成死锁。然而,Go语言的`sync.Mutex`并不支持这一特性,其设计理念认为可重入锁往往指向代码设计问题,鼓励开发者重构代码以避免此类需求。文章进一步解释了这种设计理念背后的原因,并提供了替代方案示例。总体而言,Go语言试图从设计层面避免潜在的代码问题,尽管这可能会增加一定的开发复杂性。
golang为什么不支持可重入锁?
|
7月前
|
Go
Golang 中的互斥锁是什么?
# go # programming # beginners # architecture
|
7月前
|
存储 安全 Go
Golang分段锁
Golang分段锁
105 0
|
7月前
|
存储 Go
浅谈Golang互斥锁sync.Mutex
浅谈Golang互斥锁sync.Mutex
59 0
|
7月前
|
存储 编译器 Go
Golang底层原理剖析之互斥锁sync.Mutex
Golang底层原理剖析之互斥锁sync.Mutex
117 0
|
Go
Golang 语言标准库 sync 包的 RWMutex 读写互斥锁怎么使用?
Golang 语言标准库 sync 包的 RWMutex 读写互斥锁怎么使用?
78 0