多线程读写共享变量的几种处理模式

将共享变量的读写放到一个 goroutine 中,其它 goroutine 通过 channel 进行读写操作,这种方式有很多好处。

package bank

var deposits = make(chan int) // send amout to deposit
var balances = make(chan int) // receive balance

func Deposit(amount int) {
    deposits <- amount
}

func Balance() int {
    return <-balances
}

func teller() {
    var balance int // balance 只在 teller 中可以访问
    for {
        select {
        case amount := <-deposits:
            balance += amount
        case balances <- balance:
        }
    }
}

func init() {
    go teller()
}

第二种就是最常见的互斥了,共享变量要互斥访问
可以用个数为 1 的信号量(semaphore)实现互斥

var (
    sema    = make(chan struct{}, 1)
    balance int
)

func Deposit(amout int) {
    sema <- struct{}{} // acquire token
    balance = balance + amout
    <-sema // release token
}

func Balance() int {
    sema <- struct{}{} // acquire token
    b := balance
    <-sema // release token
    return b
}

go 内部提供了互斥操作

import "sync"

var (
    mu      sync.Mutex
    balance int
)

func Deposit(amount int) {
    mu.Lock()
    defer mu.Unlock()
    balance = balance + amount
}

func Balance() int {
    mu.Lock()
    defer mu.Unlock()
    return balance
}

为什么 Balance 也需要互斥,多线程同时读取一个变量有问题吗??? Balance 也需要互斥是为了防止在写的过程中进行读取。如果在读的过程中没有线程在写多线程同时读取是没有问题的。所以为了提高读取的并发量可以用读写锁改写

var mu sync.RWMutex
var balance int
 func Balance() int {
     mu.RLock() // readers lock
     defer mu.RUnlock()
     return balance
}

Deposit 不变,Balance 只获取 RLock 读锁,只要没有线程在写,多个线程可同时获得 RLock 锁。

Do not communicate by sharing memory. Instead, share memory by communicating.

sync 库和 channel 的选择要看场景,哪种更简单更适合就用哪种。大部分场景可用 channel 实现。

你可能感兴趣的:(多线程读写共享变量的几种处理模式)