开发者

Go语言开发保证并发安全实例详解

目录
  • 什么是并发安全?
  • Mutex
    • 悲观锁
    • 乐观锁
    • 版本号机制
    • CAS
  • 互斥锁
    • 读写互斥锁

      什么是并发安全?

      高并发场景下,进程、线程(协程)可能会发生资源竞争,导致数据脏读、脏写、死锁等问题,为了避免此类问题的发生,就有了并发安全。

      这里举一个简单的例子:

       var data int 
       go func() {
         data++ 
       }(gdfRhvEg) 
       if data == 0 { 
         fmt.Printf("the value is %v.\n", data) 
       }
      

      在这段代码中

      第2行go关键字开启了一个新的协程,来执行data++操作

      第5行,对data变量进行了读取判断的操作

      以上两部是由2个不同线程/协程运行,且没有任何措施保证执行顺序,所以执行结果是不确定的。

      • 没有输出。(第3行是在第5行之前执行的)
      • 输出 the value is 0。(第5行和第6行在第3行之前执行)
      • 输出 the value is 1。(第5行在第3行之前执行,但第3行在第6行之前执行)

      Go如何保证并发安全

      目前了解到的,大概有这3种,Mutexpython、Channel、Atomic

      Mutex

      加锁应该是最常见的并发控制方法,一般分成两种,乐观锁和悲观锁

      锁是由操作系统的调度器来实现的,锁通常用来保护一段逻辑,

      悲观锁

      悲观锁是一种悲观思想,它总认为最坏的情况可能会出现。不管意料之外的结果是否会发生,只要编程客栈存在发生的可能,就在操作这个资源之前先上锁。例如互斥锁读写锁都是悲观锁。

      在go中,除了automic,其它都是悲观锁

      悲观锁应该都是由操作系统的调度器来实现的,通常用来保护一段逻辑,主要是通过阻塞其它线程,保证当前时刻只有一个线程在对资源进行操作,因此性能相对较差,浪费了计开发者_Python开发算机多核的优势。

      乐观锁

      乐观锁的思想与悲观锁的思想相反,它总认为资源和数据不会被别人所修改,所以读取不会上锁,但是乐观锁在进行写入操作的时候会判断当前数据是否被修改过

      乐观锁的实现方案主要包含CAS版本号机制

      乐观锁适用于多读的场景,可以提高吞吐量。

      版本号机制

      通过在数据表中,增加一个版本号字段,当数据发生更新时,版本号值发生改变。 例如一个线程A想要更新变量s的值,在读取s的值的同时读取版本号,在提交更新时,用之前读到的版本号值与当前的版本号值进行比对,当且仅当版本号值一致时,才会触发更新,否则不断进行重试,直到更新成功。

      CAS

      CAS全名为Compare And Swap,即比较与转换,是一种有名的无锁算法。在不使用锁的情况下,实现多线程之间的变量同步,也就是在没有线程被阻塞的情况下实现变量的同步,所以也叫非阻塞同步,

      互斥锁

      GO使用Sync包的Mutex类型来实现互斥锁,它能保证同时只有一个goroutine可以访问资源。

      func sample() {
      	var l sync.Mutex
      	l.Lock()
              defer l.Unlock()
      编程客栈	// so something
      }
      

      读写互斥锁

      GO使用Sync包的RWMutex类型来实现互斥锁。当我们去并发的读取一个资源,只要数据没有发生写入,是没必要加锁的。因此读多写少的情况下,使http://www.devze.com用读写互斥锁是更好的选择,性能更好。

      读写锁分为两种:读锁和写锁。

      当一个goroutine获取读锁之后,其他的goroutine如果是获取读锁可以顺利获得,如果是获取写锁就会等待;

      当一个goroutine获取写锁之后,其他的goroutine无论是获取读锁还是写锁都会等待。

      package main
      import (
      	"fmt"
      	"sync"
      	"time"
      )
      var (
      	wg     sync.WaitGroup
      	rwlock sync.RWMutex
      )
      func write() {
      	rwlock.Lock() // 加写锁
      	time.Sleep(10 * time.Millisecond)
      	rwlock.Unlock() // 解写锁
      	wg.Done()
      }
      func read() {
      	rwlock.RLock() // 加读锁
      	time.Sleep(time.Millisecond)
      	rwlock.RUnlock() // 解读锁
      	wg.Done()
      }
      func main() {
      	start := time.Now()
      	//读多
      	for i := 0; i < 1000; i++ {
      		wg.Add(1)
      		go read()
      	}
      	//写少
      	for i := 0; i < 10; i++ {
      		wg.Add(1)
      		go write()
      	}
      	wg.Wait()
      	end := time.Now()
      	fmt.Println(end.Sub(start))
      }

      以上就是Go语言开发保证并发安全实例详解的详细内容,更多关于Go保证并发安全的资料请关注我们其它相关文章!

      0

      上一篇:

      下一篇:

      精彩评论

      暂无评论...
      验证码 换一张
      取 消

      最新开发

      开发排行榜