网站建设入门书籍,建站之星和凡科建站哪个系统好,电商网站 建设目标详细说明,电子商务工作室经营范围✍个人博客#xff1a;Pandaconda-CSDN博客 #x1f4e3;专栏地址#xff1a;http://t.csdnimg.cn/UWz06 #x1f4da;专栏简介#xff1a;在这个专栏中#xff0c;我将会分享 Golang 面试中常见的面试题给大家~ ❤️如果有收获的话#xff0c;欢迎点赞#x1f44d;收藏… ✍个人博客Pandaconda-CSDN博客 专栏地址http://t.csdnimg.cn/UWz06 专栏简介在这个专栏中我将会分享 Golang 面试中常见的面试题给大家~ ❤️如果有收获的话欢迎点赞收藏您的支持就是我创作的最大动力 10. G o 可重入锁如何实现
概念
可重入锁又称为递归锁是指在同一个线程在外层方法获取锁的时候在进入该线程的内层方法时会自动获取锁不会因为之前已经获取过还没释放再次加锁导致死锁。
为什么 Go 语言中没有可重入锁
Mutex 不是可重入的锁。Mutex 的实现中没有记录哪个 goroutine 拥有这把锁。理论上任何 goroutine 都可以随意地 Unlock 这把锁所以没办法计算重入条件并且Mutex 重复 Lock 会导致死锁。
如何实现可重入锁
实现一个可重入锁需要这两点 记住持有锁的线程 统计重入的次数
package main
import (bytesfmtruntimestrconvsyncsync/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.recursionreturn}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.Mutex 或 sync.RWMutex 的它提供了 Wait()、Signal() 和 Broadcast() 三个方法。 Wait()释放锁并阻塞当前 Goroutine直到调用 Signal() 或 Broadcast() 并重新获得锁。在阻塞期间Goroutine 处于等待状态并且不会消耗 CPU 资源。 Signal()唤醒一个等待中的 Goroutine。 Broadcast()唤醒所有等待中的 Goroutine。
下面是一个简单的示例展示了如何使用 sync.Cond 实现多个 Goroutine 之间的通信
package main
import (fmtsynctime
)
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() 方法则可以提高程序的效率但是需要确保程序的正确性。在实际应用中应该根据具体情况选择合适的方法。