golang的同步机制

Go 语言为并发编程提供了丰富的同步机制,这些机制设计得既简洁又强大,旨在简化并确保在多线程环境(goroutine)中对共享资源的访问安全。以下是 Go 语言中主要的同步机制:

通道(Channels):

在 Go 中,通道是类型化的通信机制,用于 goroutine 之间的同步和数据交换。
通过通道发送和接收数据可以实现生产者-消费者模式、流水线处理等并发模式,并且隐式地执行了同步操作,使得在进行数据传输时能确保数据一致性。

package main

import "fmt"

func main() {
    messages := make(chan string)

    go func() { // 发送者 goroutine
        messages <- "ping"
    }()

    msg := <-messages // 接收者 goroutine
    fmt.Println(msg) // 输出:ping
}

互斥锁(sync.Mutex):

sync.Mutex 是最基本的互斥锁类型,它提供 Lock() 和 Unlock() 方法来保护临界区代码。
当一个 goroutine 获得了锁后,其他试图获取该锁的 goroutine 将被阻塞直到第一个 goroutine 释放锁为止。

package main

import (
	"fmt"
	"sync"
)

var count int
var mutex sync.Mutex

func increment() {
	mutex.Lock()
	count++
	mutex.Unlock()
}

func main() {
	var wg sync.WaitGroup
	for i := 0; i < 1000; i++ {
		wg.Add(1)
		go func() {
			increment()
			wg.Done()
		}()
	}
	wg.Wait()
	fmt.Println(count) // 输出:1000
}

读写互斥锁(sync.RWMutex):

提供了比 Mutex 更细粒度的控制,支持多个读取者同时访问资源,但同一时间只允许一个写入者。
具有 RLock()(读取锁)、RUnlock()(释放读取锁)、Lock()(独占锁)和 Unlock()(释放独占锁)方法。

package main

import (
	"fmt"
	"sync"
)

type Counter struct {
	mu sync.RWMutex
	val int
}

func (c *Counter) Incr() {
	c.mu.Lock()
	c.val++
	c.mu.Unlock()
}

func (c *Counter) Read() int {
	c.mu.RLock()
	defer c.mu.RUnlock()
	return c.val
}

func main() {
	counter := &Counter{val: 0}
	for i := 0; i < 1000; i++ {
		go counter.Incr()
	}
	
	var sum int
	for i := 0; i < 10; i++ {
		sum += counter.Read()
	}
	fmt.Println("Final value:", sum) // 输出大于等于1000的结果
}

WaitGroup(sync.WaitGroup):

WaitGroup 用于等待一组 goroutine 完成其工作。
使用 Add(delta int) 设置需要等待的任务数量,每个完成任务的 goroutine 都调用一次 Done() 减少计数器,而主线程或其他协调者可以通过 Wait() 来阻塞直到所有任务都完成了。

package main

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

func worker(id int, wg *sync.WaitGroup) {
	defer wg.Done()
	fmt.Printf("Worker %d started\n", id)
	time.Sleep(time.Second)
	fmt.Printf("Worker %d finished\n", id)
}

func main() {
	var wg sync.WaitGroup
	for i := 1; i <= 5; i++ {
		wg.Add(1)
		go worker(i, &wg)
	}
	wg.Wait()
	fmt.Println("All workers have finished")
}

Once(sync.Once):

Once 类型保证某个动作在其生命周期内仅执行一次。
使用 Do(f func()) 方法执行给定的函数 f,即便在多个 goroutine 同时调用 Do 方法的情况下,f 函数也只会被执行一次。

package main

import (
	"fmt"
	"sync"
)

var once sync.Once
var message string

func setup() {
	message = "Hello, World!"
}

func printMessage() {
	once.Do(setup)
	fmt.Println(message)
}

func main() {
	printMessage()
	printMessage() // 第二次调用不会重新执行 setup 函数
}

原子操作(sync/atomic 包):

提供了一系列无锁的原子操作,如原子增加、减少、交换、比较并交换等,适用于低级别的同步需求,尤其是在更新不需要复杂锁定逻辑的简单数值变量时。

package main

import (
	"fmt"
	"sync/atomic"
)

var count int64

func increment() {
	atomic.AddInt64(&count, 1)
}

func main() {
	for i := 0; i < 1000; i++ {
		go increment()
	}
	
	time.Sleep(time.Second) // 确保所有goroutine完成操作
	fmt.Println(atomic.LoadInt64(&count)) // 输出:1000
}

Cond(sync.Cond):

Cond 可以在满足特定条件时唤醒等待的 goroutine,允许基于条件的同步。
使用 L 字段关联一个互斥锁,然后使用 Broadcast()、Signal() 和 Wait() 方法进行条件通知和等待。
以下是一个使用 sync.Cond 的简单示例,它展示了如何在一个 goroutine 中修改共享资源状态,并通过条件变量通知其他等待该条件的 goroutine:

package main

import (
	"fmt"
	"sync"
)

var count int
var cond = sync.NewCond(new(sync.Mutex))

func incrementAndNotify() {
	cond.L.Lock()
	defer cond.L.Unlock()

	for count < 5 { // 当计数未达到5时继续循环
		fmt.Printf("Count is %d, waiting for condition...\n", count)
		cond.Wait() // 当条件不满足时挂起当前goroutine
	}

	fmt.Println("Count reached 5, incrementing...")
	count++
	fmt.Println("Incremented count to:", count)
	// 条件满足后,唤醒所有等待的goroutine
	cond.Broadcast()
}

func main() {
	go incrementAndNotify()

	// 修改共享资源状态
	cond.L.Lock()
	count = 4
	fmt.Println("Setting count to 4 and signaling condition.")
	cond.Signal() // 唤醒一个等待的goroutine(这里可能有一个或多个在等待)
	cond.L.Unlock()

	time.Sleep(time.Second) // 确保goroutine有足够时间处理信号

	cond.L.Lock()
	if count != 5 {
		fmt.Println("Final count should be 5 but it's not")
	} else {
		fmt.Println("Final count is correct (5)")
	}
	cond.L.Unlock()
}

在这个例子中,我们创建了一个全局的 sync.Cond 对象。incrementAndNotify 函数会一直等待直到计数器达到 5。当主函数设置计数器为 4 并发送一个信号时,等待条件的 goroutine 将被唤醒并执行后续逻辑。

以上同步机制构成了 Go 语言并发编程的基础工具集,它们共同作用于解决并发环境下的数据竞争、顺序依赖等问题,保证程序在多线程环境中的正确性和性能表现。

  • 6
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值