public User GetUserFromId(int id) { try { this.readerWriterLock.EnterReadLock(); if (this.userCache.ContainsKey(id)) { return(this.userCache[id]); } } finally { this.readerWriterLock.ExitReadLock(); } // We don't use a global lock while reading the user from the table // We lock only the 'id' that we are trying to retrieve. For each id, // we use a dummy lock object. This lock object is used to synchronize // the threads attempting to read the same user object. // The list of lock objects are stored in a dictionary. // We first lock the dictionary and get/create a lock object for this id. // Then we lock the lock object and check if another thread added the user to // the cache already. If so, we return. // Else we read it from the database, add the user to the user cache and exit. object lockObject = null; try { lock (this.userLockObjects) { if (!this.userLockObjects.ContainsKey(id)) { this.userLockObjects.Add(id, new object()); } lockObject = this.userLockObjects[id]; } Monitor.Enter(lockObject); if (this.userCache.ContainsKey(id)) { return(this.userCache[id]); } User user = UserDbQuery.InternalGetUserFromId(id); if (user == null) { return(null); } this.userCache.Add(id, user); if (user.UserData.UserType != UserType.Group) { this.phoneUserCache.Add(user.UserData.PhoneNumber, user); } return(user); } finally { Monitor.Exit(lockObject); lock (this.userLockObjects) { if (this.userLockObjects.ContainsKey(id)) { this.userLockObjects.Remove(id); } } } }
public List <User> GetUsersFromPhones(List <string> phoneNumbers) { List <User> users = new List <User>(); List <string> notFoundPhoneNumbers = new List <string>(); Dictionary <int, User> addedUsers = new Dictionary <int, User>(); foreach (string phoneNumber in phoneNumbers) { try { this.readerWriterLock.EnterReadLock(); if (this.phoneUserCache.ContainsKey(phoneNumber)) { User user = this.phoneUserCache[phoneNumber]; if (!addedUsers.ContainsKey(user.UserData.Id)) { users.Add(this.phoneUserCache[phoneNumber]); addedUsers.Add(user.UserData.Id, user); } } else { notFoundPhoneNumbers.Add(phoneNumber); } } finally { this.readerWriterLock.ExitReadLock(); } } List <User> notFoundUsers = UserDbQuery.InternalGetUsersFromPhones(notFoundPhoneNumbers); foreach (User u in notFoundUsers) { if (!addedUsers.ContainsKey(u.UserData.Id)) { users.Add(u); addedUsers.Add(u.UserData.Id, u); } } foreach (User user in notFoundUsers) { object lockObject = null; try { lock (this.userLockObjects) { if (!this.userLockObjects.ContainsKey(user.UserData.Id)) { this.userLockObjects.Add(user.UserData.Id, new object()); } lockObject = this.userLockObjects[user.UserData.Id]; } Monitor.Enter(lockObject); if (this.userCache.ContainsKey(user.UserData.Id)) { continue; } this.userCache.Add(user.UserData.Id, user); this.phoneUserCache.Add(user.UserData.PhoneNumber, user); } finally { Monitor.Exit(lockObject); lock (this.userLockObjects) { if (this.userLockObjects.ContainsKey(user.UserData.Id)) { this.userLockObjects.Remove(user.UserData.Id); } } } } return(users); }