为了解释一下我的环境,我需要跟踪用户和每个用户拥有的一系列内容。这是高度动态的,尽管基本用户的数量总是高于他们拥有的东西的集合。我决定采用这样的方式:
private static Dictionary<string, Dictionary<string, MyClass>> ActiveUsers =
new Dictionary<string, Dictionary<string, MyClass>>();
在这种情况下,父词典的TKey
是用户的connectionId,内部词典的TKey
是表示MyClass
之前我的意思是ActiveUsers将(希望)拥有大量的TKeys,而TValues通常会持有少于10个项目。由于这些项是相关的,当用户断开连接时,它将从父词典中删除,并且父词典中的所有其他项目将在其内部词典中搜索某些值,如果存在,这些项目将被删除。
这个字段将不断访问(非常频繁),并且我试图获得最佳性能。
这是一种更好的方法(与性能相关),而不是创建一个ID为Dictionary<string, MyClass>
的类作为字段并保存此类的列表吗?
这样的事情会更好吗?
public class ActiveUsersManager
{
public string Id{ get; private set; }
public Dictionary<string, MyClass> ActiveUsers { get; private set; }
public ActiveUsersManager(string connectionId)
{
Id = connectionId;
ActiveUsers = new Dictionary<string, MyClass>();
}
}
//In another class
private static List<ActiveUsersManager> ActiveUsers = new List<ActiveUsersManager>();
如果有帮助,ActiveUsers是ASP.NET控制器中的静态字段。
编辑:回答评论
这个词典的用法如下:
public static MyClass GetInformation(string myId, string objectId)
{
//Validation removed
Dictionary<string, MyClass> dictionaryResult = null;
MyClass result = null;
if (!ActiveUsers.TryGetValue(myId, out dictionaryResult)) //check if the user was already added to the Dictionary
{
dictionaryResult = new Dictionary<string, MyClass>();
result = new MyClass(objectId);
dictionaryResult.Add(objectId, result);
ActiveUsers.Add(myId, dictionaryResult);
}
else if (!dictionaryResult.TryGetValue(objectId, out result)) //otherwise check if the user already has this item
{
result = new MyClass(objectId);
dictionaryResult.Add(objectId, result);
ActiveUsers.Add(myId, dictionaryResult);
}
//else everything is already set-up
return result;
}
编辑:显示内部项目如何删除的代码
public static void RemoveUserAndSessions(string userId, string objectId)
{
ActiveUsers.Remove(userId);
foreach (Dictionary<string, MyClass> dic in ActiveUsers.Values)
dic.Remove(objectId);
}
这是我工作的第一个ASP.NET应用程序,我以前没有完成任何涉及字典的多线程,我怎么能让这个线程安全呢?
编辑:试图让事情更清楚。
我不想透露细节,但我想他们需要了解背后的原因。这是一个聊天应用程序。每个用户都存储在ActiveUsers中,以跟踪活动用户。每个用户都有一个与其连接的客户端的字典,MyClass对象包含客户端进行通信所需的一组属性。一旦用户断开连接,必须立即删除所有活动会话,因此删除方法。我想这可以通过创建另一个类来保存字典中的活动会话并在原始字典中使用此类来完成 如上所述,我将查看ConcurrentDictionary
答案 0 :(得分:0)
我强烈建议更像这样构建数据:
private static ConcurrentDictionary<string, ActiveUser> ActiveUsers { get; set; }
private class ActiveUser {
public int ConnectionId { get; set; }
public Dictionary<string, MyClass> MyClasses { get; set; } = new Dictionary<string, MyClass>();
}
// . . .
Dictionary<string, ActiveUser> ActiveUsers = new Dictionary<string, ActiveUser>();
(注意:如果您使用的是C#&lt; 6,只需在ActiveUser
构造函数中初始化字典)
对于您的删除方法,您可能希望执行以下操作:
public static void RemoveUserAndSessions(string userId, string objectId)
{
ActiveUsers.Remove(userId);
Parallel.ForEach(ActiveUsers.Values, dic => dic.Remove(objectId));
}
编辑:为了清晰起见,重命名了一些内容。
答案 1 :(得分:0)
我在设计中看到的性能视角的主要问题是需要在RemoveUserAndSessions
方法中迭代所有字典。这是一个没有这个问题的示例设计
static class UserManager
{
private static readonly Dictionary<string, User> ActiveUsers = new Dictionary<string, User>();
public static User GetInformation(string userId)
{
lock (ActiveUsers)
{
User user;
if (!ActiveUsers.TryGetValue(userId, out user))
ActiveUsers.Add(userId, user = new User(userId));
return user;
}
}
public static void RemoveUserAndSessions(string userId)
{
lock (ActiveUsers)
{
User user;
if (ActiveUsers.TryGetValue(userId, out user))
{
ActiveUsers.Remove(userId);
user.EndAllSessions();
}
}
}
public static void StartSession(string firstUserId, string secondUserId)
{
lock (ActiveUsers)
{
var firstUser = GetInformation(firstUserId);
var secondUser = GetInformation(secondUserId);
firstUser.StartSession(secondUser);
secondUser.StartSession(firstUser);
}
}
}
class User
{
private Dictionary<string, User> sessions = new Dictionary<string, User>();
public string Id { get; private set; }
public User(string id) { Id = id; }
public void StartSession(User other)
{
sessions.Add(other.Id, other);
}
public void EndSession(User other)
{
sessions.Remove(other.Id);
}
public void EndAllSessions()
{
foreach (var other in sessions.Values)
other.EndSession(this);
sessions.Clear();
}
}
这只是为了得到这个想法,希望你能把它映射到你的具体案例。