package main

import (
    "sync/atomic"
    "time"
)

type SpinLock struct {
    lock int32
}

func (sl *SpinLock) Lock() {
    for !sl.TryLock() {
        time.Sleep(1) // 等待并尝试获取锁
    }
}

func (sl *SpinLock) Unlock() {
    atomic.StoreInt32(&sl.lock, 0) // 释放锁
}

func (sl *SpinLock) TryLock() bool {
    return atomic.CompareAndSwapInt32(&sl.lock, 0, 1) // 尝试获取锁
}

func main() {
    var lock SpinLock
    lock.Lock()
    // critical section
    lock.Unlock()
}

这个例子中的SpinLock结构体有一个int32类型的lock字段,用于表示锁的状态。Lock方法会不断尝试获取锁,直到成功为止。Unlock方法会释放锁。TryLock方法会尝试获取锁,如果成功则返回true,否则返回false。

请注意,自旋锁在某些情况下可能并不是最佳选择,因为它们会持续占用CPU资源直到获取到锁。在高并发或者长时间等待锁的情况下,使用标准的互斥锁(如sync.Mutex)可能会更加高效。自旋锁更适合锁保护的临界区很小,锁被持有的时间非常短的情况。在这种情况下,自旋锁的开销可能会小于创建和管理一个goroutine的开销。在使用自旋锁时,需要仔细考虑其适用场景。

在Go语言中实现自旋锁时,runtime.Gosched()通常是一个更好的选择。runtime.Gosched()函数会让当前goroutine让出CPU,从而允许其他goroutine运行。这样可以避免当前goroutine占用过多CPU资源。

在 Go 语言中,runtime.Gosched() 函数的作用是让当前 goroutine 让出 CPU,以便其他的 goroutine 获得执行的机会1。其原理是将当前 Goroutine 放回到队列中,等待下一次调度。

package main

import (
    "fmt"
    "runtime"
    "time"
)

func main() {
    go func() {
        for i := 0; i < 3; i++ {
            fmt.Println("Goroutine 1 is running,", i)
            runtime.Gosched()
        }
    }()

    go func() {
        for i := 0; i < 3; i++ {
            fmt.Println("Goroutine 2 is running,", i)
            runtime.Gosched()
        }
    }()

    time.Sleep(100 * time.Millisecond)
}

发表回复