Go语言并发编程中Mutex 允许自旋的条件

发布于 1周前 作者 wuwangju 最后一次编辑是 5天前 来自 问答

Go语言并发编程中Mutex 允许自旋的条件:

1、锁已被占用,并且锁不处于饥饿模式。 2、积累的自旋次数小于最大自旋次数(active_spin=4)。 3、CPU 核数大于 1。 4、 有空闲的 P。 5、当前 Goroutine 所挂载的 P 下,本地待运行队列为空。

1 回复

在Go语言的并发编程中,sync.Mutex 本身并不直接支持自旋(Spinning)作为其内置行为。sync.Mutex 的实现主要依赖于操作系统提供的锁机制(如 futex 在 Linux 上),这些锁在资源争用较高时会导致线程(或 goroutine)的阻塞和调度,这可能会引入额外的上下文切换开销。

然而,如果你想要在 Go 中实现一个支持自旋的锁,你可以通过自定义锁结构来实现,结合 Go 的 sync/atomic 包来操作内存中的标志位,以及使用 time.Sleep 或循环等待(自旋)来尝试获取锁,直到成功或达到某个条件(如自旋次数限制)。

以下是一个简单的示例,展示了如何实现一个基本的自旋锁:

package main

import (
	"sync/atomic"
	"time"
	"fmt"
)

type SpinLock int32

func (sl *SpinLock) Lock() {
	for !atomic.CompareAndSwapInt32((*int32)(sl), 0, 1) {
		// 这里可以添加自旋策略,比如简单的循环或带有短暂休眠的循环
		// 这里为了简化,我们仅使用简单的循环自旋
	}
}

func (sl *SpinLock) Unlock() {
	atomic.StoreInt32((*int32)(sl), 0)
}

func main() {
	var lock SpinLock

	go func() {
		lock.Lock()
		fmt.Println("Goroutine 1 locked the lock")
		time.Sleep(2 * time.Second) // 模拟长时间持有锁
		lock.Unlock()
		fmt.Println("Goroutine 1 unlocked the lock")
	}()

	time.Sleep(1 * time.Second) // 确保第一个 goroutine 已经获取了锁

	go func() {
		lock.Lock()
		fmt.Println("Goroutine 2 locked the lock")
		lock.Unlock()
		fmt.Println("Goroutine 2 unlocked the lock")
	}()

	// 等待足够长的时间以观察输出
	time.Sleep(4 * time.Second)
}

注意

  1. 这里的 SpinLock 实现是非常基础的,并没有处理自旋次数过多时可能导致的 CPU 资源浪费问题。
  2. 在实际应用中,应当谨慎使用自旋锁,特别是在锁持有时间可能较长或系统负载较高的场景中,因为它可能会导致 CPU 资源的过度占用。
  3. Go 的 sync.Mutex 已经足够高效,对于大多数并发场景来说,直接使用 sync.Mutex 通常是更好的选择。如果你确实需要更细粒度的控制(如自旋),考虑使用更高级的同步机制或库。
回到顶部