在SignalR中使服务器端客户端列表线程安全
本文关键字:列表 线程 安全 客户端 服务器端 SignalR | 更新日期: 2023-09-27 18:21:15
我正在努力确保服务器端线程的安全。
目前,我正在使用List<ServerClient> m_connectedClients
,当我的客户连接或断开连接时,我会用它填充,如下所示:
public LoginStatus Connect(string connectionId, LoginRequestMessage message)
{
var status = m_databaseManager.CheckLogin(message.Username, message.Password);
if (status == LoginStatus.Success)
{
var connection = m_connectedClients.FirstOrDefault(p => p.Username == message.Username);
if (connection == null)
{
var client = m_databaseManager.GetClient(message.Username, connectionId);
m_connectedClients.Add(client);
}
else if (!connection.ConnectionIds.Contains(connectionId))
{
connection.ConnectionIds.Add(connectionId);
}
}
return status;
}
public void Disconnect(string connectionId)
{
foreach (var connection in m_connectedClients.Where(p => p.ConnectionIds.Contains(connectionId)))
{
connection.ConnectionIds.Remove(connectionId);
}
Console.WriteLine("Client disconnected... {0}", connectionId);
}
在我的场景中,在某些情况下,我想做这样的事情:
public bool IsLoggedIn(string connectionId)
{
if (m_connectedClients.Any(p => p.ConnectionIds.Contains(connectionId)))
{
return true;
}
return false;
}
因此,简单地用ConcurrentDictionary添加和删除内容可能不会起作用(我甚至不知道我会使用什么密钥,因为我每个客户端可能有多个ConnectionId,这是我在这里唯一的标识符。
我可以做的是每次访问List<ServerClient>
时都锁定它,但这会发生在很多有很多锁的地方,所以我想这是一个糟糕的主意,对吧?
另一个问题是:我必须在任何地方都保持线程安全吗?还是仅在连接(添加)和断开连接(删除)时才重要,而在实际使用该列表时不重要?
因此,简单地用ConcurrentDictionary添加和删除内容可能不会起作用(我甚至不知道我会使用什么密钥,因为我每个客户端可能有多个ConnectionId,这是我在这里唯一的标识符。
你是对的。ConcurrentDictionary对于您的用例来说可能过于有限。
我可以做的是每次访问列表时都锁定它,但这会在很多有很多锁的地方发生,所以我想这是一个糟糕的主意,对吧?
这是没有办法的:每次你访问列表时,你都应该带上一把锁。创建一个提供所需列表的功能的类,并将锁和获取锁放在那里,这样获取锁就不会分散在代码中,可能会被意外遗忘。
另一个问题是:我在任何地方都必须是线程安全的吗?还是仅在连接(添加)和断开连接(删除)时才重要,而在实际使用该列表时不重要?
线程安全是一个全局特性。如果你不是处处都是线程安全的,那么你就不是线程安全期。如果您有很多读卡器和读取操作,而只有很少的写入器和写入操作,那么可以考虑使用针对此类场景优化的ReaderWriterLockSlim类。不过,每次访问列表时都必须使用它。