返回
多房间聊天室,为什么非加锁不可?
后端
2024-01-27 04:31:34
在这场关于多房间聊天室的讨论中,有人质疑为何必须给代码逻辑加锁。本文将通过编写测试用例,论证加锁的必要性和充分性。
在上篇文章中,我们构建了一个多房间的聊天室,它允许用户加入和离开房间,并在这些房间内发送消息。我们还讨论了需要给共享数据结构加锁,以确保并发访问的正确性。
但有人可能会问:为什么一定要加锁?不加锁行不行啊?
为了回答这个问题,让我们编写一些测试用例。
第一个测试用例不使用任何锁。
func TestUnlockChatRoom(t *testing.T) {
room := &ChatRoom{
clients: make(map[string]*Client),
}
// 模拟两个客户端同时加入同一个房间
go func() {
room.Join("client1")
}()
go func() {
room.Join("client2")
}()
// 模拟其中一个客户端离开房间
room.Leave("client1")
// 检查房间中是否还有另一个客户端
if len(room.clients) != 1 {
t.Errorf("Expected 1 client in the room, got %d", len(room.clients))
}
}
运行这个测试用例,我们可能会得到以下错误:
--- FAIL: TestUnlockChatRoom (0.00s)
multiroom_test.go:22: Expected 1 client in the room, got 0
这个错误告诉我们,当我们不给代码逻辑加锁时,两个客户端可能会同时修改房间的客户端列表,导致数据不一致。
为了解决这个问题,我们可以给代码逻辑加锁。
func TestLockChatRoom(t *testing.T) {
room := &ChatRoom{
clients: make(map[string]*Client),
lock: &sync.Mutex{},
}
// 模拟两个客户端同时加入同一个房间
go func() {
room.Lock()
defer room.Unlock()
room.Join("client1")
}()
go func() {
room.Lock()
defer room.Unlock()
room.Join("client2")
}()
// 模拟其中一个客户端离开房间
room.Lock()
defer room.Unlock()
room.Leave("client1")
// 检查房间中是否还有另一个客户端
room.Lock()
defer room.Unlock()
if len(room.clients) != 1 {
t.Errorf("Expected 1 client in the room, got %d", len(room.clients))
}
}
运行这个测试用例,我们将不会得到任何错误。这意味着当我们给代码逻辑加锁时,两个客户端就不会同时修改房间的客户端列表,从而保证了数据的一致性。
通过这两个测试用例,我们可以看到,给多房间聊天室的代码逻辑加锁是必要的。不加锁可能会导致数据不一致,而加锁可以保证数据的一致性。