Golang基础--并发控制

前言

  在实际项目开发中,有时会面临同一时刻将多个goroutine作用于同一个对象的情况,此时,他们之间会发生冲突,这种情况称为数据竞态问题。例如:

复制代码
package main

import (
    "fmt"
    "time"
)

var count int

func main() {
    go CountPlus(10000)
    go CountPlus(10000)
    time.Sleep(time.Second)
    fmt.Println(count)
}
func CountPlus(times int) {
    for i := 0; i < times; i++ {
        count++
    }
}
复制代码

  正常因该会返回20000,但是结果却不是。原因在于将两个goroutine作用于同一个count。

  解决方案:加锁

复制代码
package main

import (
    "fmt"
    "sync"
    "time"
)

var count int
var lock sync.Mutex

func main() {
    go CountPlus(10000)
    go CountPlus(10000)
    time.Sleep(time.Second)
    fmt.Println(count)
}
func CountPlus(times int) {
    for i := 0; i < times; i++ {
        lock.Lock()
        count++
        lock.Unlock()
    }
}
复制代码

  声明Mutex类型的变量lock,并通过lock调用Lock()函数进行加锁,待操作完成后,在调用UnLock()解锁,在加锁和解锁函数之间的代码将受到保护,在同一时间仅有一个goroutine在执行受保护的代码。

  上述提及的Mutex为互斥锁,能很好的避免多个goroutine同时操作同一数据。

RWMutex读写互斥锁

  当某个goroutine获得读操作后,其他尝试进行读操作的goroutine也能正常获得锁,但是需要进行写操作的gioroutine会继续等待;

  当某个goroutine获得写操作的锁后,由于数据很可能发生改变,因此接下来的无论是读操作还是写操作都会继续排队等待。

  示例代码:

复制代码
package main

import (
    "fmt"
    "sync"
    "time"
)

var countTest int
var locker sync.RWMutex

func main() {
    for i := 1; i <= 3; i++ {
        go Write(i)
    }
    for i := 1; i <= 3; i++ {
        go Read(i)
    }
    time.Sleep(10 * time.Second)
    fmt.Println("countTest的值为:", countTest)
}
func Read(i int) {
    fmt.Println("读操作:", i)
    locker.RLock()
    fmt.Println(i, "读countTest的值为:", countTest)
    time.Sleep(1 * time.Second)
    locker.RUnlock()
}
func Write(i int) {
    fmt.Println("写操作", i)
    locker.Lock()
    countTest++
    fmt.Println(i, "写countTest的值为:", countTest)
    time.Sleep(1 * time.Second)
    locker.Unlock()

}
复制代码

这里解释一下代码的运行逻辑:程序一开始,就执行了写操作,其他goroutine只能等待其完成。但此时,后续的goroutine都开始执行,只不过运行到有锁的地方暂停了,因此可以看到大量“读操作”和“写操作”同时输出

  等待一秒后,3次读操作不会对数据发生更改,因此几乎同时完成操作

  再等待一秒后,2次写操作排队进行,然后完成操作

  最终,count被累加3次,值为3

WaitGroup

WaitGroup,等待一组goroutine结束.

type WaitGroup struct {
	noCopy noCopy

	// 64-bit value: high 32 bits are counter, low 32 bits are waiter count.
	// 64-bit atomic operations require 64-bit alignment, but 32-bit
	// compilers only guarantee that 64-bit fields are 32-bit aligned.
	// For this reason on 32 bit architectures we need to check in state()
	// if state1 is aligned or not, and dynamically "swap" the field order if
	// needed.
	state1 uint64
	state2 uint32
}

waitgroup对外提供三个借口,分别是

  Add():

  Wait()

  Done()

posted @   99号的格调  阅读(109)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 周边上新:园子的第一款马克杯温暖上架
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?
· 使用C#创建一个MCP客户端
点击右上角即可分享
微信分享提示