1、使用互斥锁的目的
connLock
用于保护全局的 UDP 连接conn
。无论哪个函数需要访问conn
,都必须通过connLock
进行同步
2、介绍
- 互斥锁用于确保在同一时间只有一个goroutine可以访问共享资源,防止数据竞争。
sync.Mutex
的设计目的是通过锁机制确保同一时刻只有一个协程访问共享资源。
如果被多个函数访问,那么需要将conn设置为全局变量
3、为什么要设置成全局变量
如果不同的函数使用不同的锁来保护同一个资源,反而会导致同步问题,因为锁之间无法协调,可能导致多个goroutine同时访问资源
var conn *net.UDPConn var connLock sync.Mutex
在RequestRegister函数中使用了connLock.Lock()和defer connLock.Unlock()
当RequestNotify函数也需要访问或修改conn时,同样需要加锁,因为任何对共享资源的并发访问都需要通过锁来同步。
4、如何使用
因为connLock是用于保护conn的,无论哪个函数访问conn,都必须先获取这个锁。因此,在RequestNotify中使用connLock.Lock()和Unlock()是必要的,以确保在修改或读取conn时不会有其他goroutine同时进行,避免竞态条件。
在使用互斥锁时,必须确保在函数的所有退出路径中都释放锁,包括在发生错误时。使用defer语句可以很好地处理这一点,因为它会在函数返回前执行Unlock,无论函数是正常返回还是因为错误提前返回。
connLock.Lock()
defer connLock.Unlock()
在RequestRegister函数中使用Lock和UnLock之后,在RequestNotify函数中可以使用相同的connLock进行加锁和解锁,可以有效保护共享资源conn,避免并发访问导致的问题
5、关键原理说明
-
锁的作用域:
-
connLock
保护的是conn
这个共享资源,而不是特定函数 -
任何需要访问
conn
的代码路径(无论是注册、通知还是其他操作)必须通过同一把锁
-
-
协程安全:
-
多个协程可能同时触发
RequestRegister
和RequestNotify
-
锁机制确保同一时间只有一个协程操作连接,避免并发写入导致的崩溃或数据混乱
-
-
死锁预防:
-
使用
defer connLock.Unlock()
确保即使发生 panic 或提前 return 也能释放锁 -
避免在锁内调用其他可能获取同一锁的函数(防止递归锁)
-