• 并发安全和锁


    1、竞态问题

    多个 goroutine 同时操作一个资源(临界区)的情况,这种情况下就会发生竞态问题。

    2、互斥锁

    互斥锁是一种常用的控制共享资源访问的方法,它能够保证同一时间只有一个 goroutine 可以访问共享资源。Go 语言中使用sync包中提供的Mutex类型来实现互斥锁。

    sync.Mutex提供了两个方法供我们使用。

    方法名功能
    func (m *Mutex) Lock() 获取互斥锁
    func (m *Mutex) Unlock() 释放互斥锁

    package main
    
    import (
    	"fmt"
    	"sync"
    )
    
    // sync.Mutex
    
    var (
    	x int64
    
    	wg sync.WaitGroup // 等待组
    
    	m sync.Mutex // 互斥锁
    )
    
    // add 对全局变量x执行5000次加1操作
    func add() {
    	for i := 0; i < 5000; i++ {
    		m.Lock() // 修改x前加锁
    		x = x + 1
    		m.Unlock() // 改完解锁
    	}
    	wg.Done()
    }
    
    func main() {
    	wg.Add(2)
    
    	go add()
    	go add()
    
    	wg.Wait()
    	fmt.Println(x)
    }
    

    3、读写互斥锁

    互斥锁是完全互斥的,但是实际上有很多场景是读多写少的,当我们并发的去读取一个资源而不涉及资源修改的时候是没有必要加互斥锁的,这种场景下使用读写锁是更好的一种选择。读写锁在 Go 语言中使用sync包中的RWMutex类型。

    sync.RWMutex提供了以下5个方法。

    方法名功能
    func (rw *RWMutex) Lock() 获取写锁
    func (rw *RWMutex) Unlock() 释放写锁
    func (rw *RWMutex) RLock() 获取读锁
    func (rw *RWMutex) RUnlock() 释放读锁
    func (rw *RWMutex) RLocker() Locker 返回一个实现Locker接口的读写锁

    读写锁分为两种:读锁和写锁。当一个 goroutine 获取到读锁之后,其他的 goroutine 如果是获取读锁会继续获得锁,如果是获取写锁就会等待;而当一个 goroutine 获取写锁之后,其他的 goroutine 无论是获取读锁还是写锁都会等待。

    var (
    	x       int64
    	wg      sync.WaitGroup
    	mutex   sync.Mutex
    	rwMutex sync.RWMutex
    )
    
    // writeWithLock 使用互斥锁的写操作
    func writeWithLock() {
    	mutex.Lock() // 加互斥锁
    	x = x + 1
    	time.Sleep(10 * time.Millisecond) // 假设读操作耗时10毫秒
    	mutex.Unlock()                    // 解互斥锁
    	wg.Done()
    }
    
    // readWithLock 使用互斥锁的读操作
    func readWithLock() {
    	mutex.Lock()                 // 加互斥锁
    	time.Sleep(time.Millisecond) // 假设读操作耗时1毫秒
    	mutex.Unlock()               // 释放互斥锁
    	wg.Done()
    }
    
    // writeWithLock 使用读写互斥锁的写操作
    func writeWithRWLock() {
    	rwMutex.Lock() // 加写锁
    	x = x + 1
    	time.Sleep(10 * time.Millisecond) // 假设读操作耗时10毫秒
    	rwMutex.Unlock()                  // 释放写锁
    	wg.Done()
    }
    
    // readWithRWLock 使用读写互斥锁的读操作
    func readWithRWLock() {
    	rwMutex.RLock()              // 加读锁
    	time.Sleep(time.Millisecond) // 假设读操作耗时1毫秒
    	rwMutex.RUnlock()            // 释放读锁
    	wg.Done()
    }
    
    func do(wf, rf func(), wc, rc int) {
    	start := time.Now()
    	// wc个并发写操作
    	for i := 0; i < wc; i++ {
    		wg.Add(1)
    		go wf()
    	}
    
    	//  rc个并发读操作
    	for i := 0; i < rc; i++ {
    		wg.Add(1)
    		go rf()
    	}
    
    	wg.Wait()
    	cost := time.Since(start)
    	fmt.Printf("x:%v cost:%v\n", x, cost)
    
    }

    使用读写互斥锁在读多写少的场景下能够极大地提高程序的性能。不过需要注意的是如果一个程序中的读操作和写操作数量级差别不大,那么读写互斥锁的优势就发挥不出来。

    参考:Go语言基础之并发 | 李文周的博客 (liwenzhou.com)

  • 相关阅读:
    Windows Phone7官方更新 增加复制粘贴
    使Apache(Linux)支持Silverlight
    WPF触摸屏项目案例(放置在奔驰公司触摸屏展厅)
    项目开发项目管理(转)
    诺基亚WP7手机界面曝光
    Windows 8十大传言:令苹果坐立不安
    如何为 iPad 打造速度超快的 HTML5 软件
    Silverlight5.0正式发布附下载地址
    我们的案例请访问我们团队官网http://Silverlighter.net
    【转】NTFS3G的安装和配置
  • 原文地址:https://www.cnblogs.com/mango1997/p/16039552.html
Copyright © 2020-2023  润新知