当前位置: 首页> 教育> 高考 > 【Golang 面试 - 进阶题】每日 3 题(四)

【Golang 面试 - 进阶题】每日 3 题(四)

时间:2025/7/13 1:07:37来源:https://blog.csdn.net/Newin2020/article/details/140754621 浏览次数:0次

✍个人博客:Pandaconda-CSDN博客

📣专栏地址:http://t.csdnimg.cn/UWz06

📚专栏简介:在这个专栏中,我将会分享 Golang 面试中常见的面试题给大家~
❤️如果有收获的话,欢迎点赞👍收藏📁,您的支持就是我创作的最大动力💪

10. G o 可重入锁如何实现?

概念:

可重入锁又称为递归锁,是指在同一个线程在外层方法获取锁的时候,在进入该线程的内层方法时会自动获取锁,不会因为之前已经获取过还没释放再次加锁导致死锁。

为什么 Go 语言中没有可重入锁?

Mutex 不是可重入的锁。Mutex 的实现中没有记录哪个 goroutine 拥有这把锁。理论上,任何 goroutine 都可以随意地 Unlock 这把锁,所以没办法计算重入条件,并且Mutex 重复 Lock 会导致死锁。

如何实现可重入锁?

实现一个可重入锁需要这两点:

  • 记住持有锁的线程

  • 统计重入的次数

package main
import ("bytes""fmt""runtime""strconv""sync""sync/atomic"
)
type ReentrantLock struct {sync.Mutexrecursion int32 // 这个goroutine 重入的次数owner     int64 // 当前持有锁的goroutine id
}
// Get returns the id of the current goroutine.
func GetGoroutineID() int64 {var buf [64]bytevar s = buf[:runtime.Stack(buf[:], false)]s = s[len("goroutine "):]s = s[:bytes.IndexByte(s, )]gid, _ := strconv.ParseInt(string(s), 10, 64)return gid
}
func NewReentrantLock() sync.Locker {res := &ReentrantLock{Mutex:     sync.Mutex{},recursion: 0,owner:     0,}return res
}
// ReentrantMutex 包装一个Mutex,实现可重入
type ReentrantMutex struct {sync.Mutexowner     int64 // 当前持有锁的goroutine idrecursion int32 // 这个goroutine 重入的次数
}
func (m *ReentrantMutex) Lock() {gid := GetGoroutineID()// 如果当前持有锁的goroutine就是这次调用的goroutine,说明是重入if atomic.LoadInt64(&m.owner) == gid {m.recursion++return}m.Mutex.Lock()// 获得锁的goroutine第一次调用,记录下它的goroutine id,调用次数加1atomic.StoreInt64(&m.owner, gid)m.recursion = 1
}
func (m *ReentrantMutex) Unlock() {gid := GetGoroutineID()// 非持有锁的goroutine尝试释放锁,错误的使用if atomic.LoadInt64(&m.owner) != gid {panic(fmt.Sprintf("wrong the owner(%d): %d!", m.owner, gid))}// 调用次数减1m.recursion--if m.recursion != 0 { // 如果这个goroutine还没有完全释放,则直接返回return}// 此goroutine最后一次调用,需要释放锁atomic.StoreInt64(&m.owner, -1)m.Mutex.Unlock()
}
func main() {var mutex = &ReentrantMutex{}mutex.Lock()mutex.Lock()fmt.Println(111)mutex.Unlock()mutex.Unlock()
}

 11. Cond 是什么?

在 Go 语言中,sync.Cond 是一个条件变量的实现,它可以在多个 Goroutine 之间传递信号和数据。条件变量是一种同步机制,用于解决某些 Goroutine 需要等待某个事件或条件发生的问题。

sync.Cond 是基于 sync.Mutexsync.RWMutex 的,它提供了 Wait()Signal()Broadcast() 三个方法。

  • Wait():释放锁并阻塞当前 Goroutine,直到调用 Signal()Broadcast() 并重新获得锁。在阻塞期间,Goroutine 处于等待状态并且不会消耗 CPU 资源。

  • Signal():唤醒一个等待中的 Goroutine。

  • Broadcast():唤醒所有等待中的 Goroutine。

下面是一个简单的示例,展示了如何使用 sync.Cond 实现多个 Goroutine 之间的通信:

package main
import ("fmt""sync""time"
)
func main() {var mutex sync.Mutexcond := sync.NewCond(&mutex)ready := falsefor i := 0; i < 5; i++ {go func(i int) {mutex.Lock()defer mutex.Unlock()for !ready {cond.Wait()}fmt.Printf("Goroutine %d is now running\n", i)}(i)}time.Sleep(time.Second * 1)mutex.Lock()ready = truecond.Broadcast()mutex.Unlock()time.Sleep(time.Second * 1)
}

在这个例子中,我们创建了一个 sync.Cond 实例,将它和一个 sync.Mutex 关联起来。然后我们启动了 5 个 Goroutine,在其中每个 Goroutine 上等待一个条件变量。我们在主 Goroutine 中等待一秒钟,然后发送一个 Broadcast 信号,通知所有等待中的 Goroutine 可以开始执行了。在接收到 Broadcast 信号后,所有 Goroutine 将开始执行,并输出一些消息。

12. B r oadcast 和 Signal 区别

在 Go 语言中,sync.Cond 类型提供了 Broadcast()Signal() 两个方法来唤醒等待条件变量的 Goroutine。这两个方法的区别在于:

  • Signal() 方法只会唤醒等待条件变量的一个 Goroutine,具体哪个 Goroutine 会被唤醒是不确定的。如果多个 Goroutine 等待同一个条件变量,那么只会有一个 Goroutine 被唤醒,其他 Goroutine 仍然会继续等待条件变量。

  • Broadcast() 方法会唤醒所有等待条件变量的 Goroutine,使它们都开始运行。如果多个 Goroutine 等待同一个条件变量,那么所有 Goroutine 都会被唤醒。

一般来说,使用 Signal() 方法可以提高程序的效率,因为只需要唤醒一个 Goroutine,其他 Goroutine 仍然会等待条件变量,不会消耗 CPU 资源。但是,如果有多个 Goroutine 都需要同时等待条件变量,那么使用 Broadcast() 方法才能保证它们都能被唤醒,否则可能会出现死锁等问题。

总之,Broadcast() 方法是一种安全可靠的方法,但是可能会导致一些性能问题。而 Signal() 方法则可以提高程序的效率,但是需要确保程序的正确性。在实际应用中,应该根据具体情况选择合适的方法。

关键字:【Golang 面试 - 进阶题】每日 3 题(四)

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

责任编辑: