Кажется, что нет практического способа удалить пустую коллекцию (даже если она синхронизирована) из параллельного словаря без проблем состояния гонки. Существуют определенные факты, которые не позволяют сделать это возможным, о чем говорилось в комментариях как к вопросу, так и к самостоятельному ответу ОП.
Однако то, что я написал в своем комментарии, казалось выполнимым, и я хотел попробовать .
Я хочу обсудить недостатки этой реализации сразу после этого, и я должен также сказать, что ваши комментарии (если они были получены) являются для меня наиболее ценными.
Во-первых, использование :
static void Main(string[] args)
{
var myDictionary = new ConcurrentDictionary<string, IList<int>>();
IList<int> myList = myDictionary.AddSelfRemovingList<string, int>("myList");
myList.Add(5);
myList.Add(6);
myList.Remove(6);
myList.Remove(5);
IList<int> existingInstance;
// returns false:
bool exists = myDictionary.TryGetValue("myList", out existingInstance);
// throws HasAlreadyRemovedSelfException:
myList.Add(3);
}
AddSelfRemovingList
- это метод расширения, облегчающий задачу.
Для обсуждения:
- Недопустимо удаление элемент из коллекции, имеющий побочный эффект удаления ссылки на коллекцию из словаря-владельца.
- Также не рекомендуется делать коллекцию устаревшей (непригодной для использования), когда все ее элементы удалены. Существует большая вероятность того, что потребитель коллекции захочет очистить и повторно заполнить коллекцию, и эта реализация не позволяет этого.
- Это заставляет использовать
IList<T>
абстракцию и пользовательскую реализацию над * 1021. *
Хотя это обеспечивает реальный потокобезопасный способ удаления только что опустошенной коллекции из словаря, кажется, что у нее больше минусов, чем плюсов. Это следует использовать только в закрытом контексте, где коллекции внутри параллельного словаря открыты для внешнего использования, и где немедленное удаление коллекции после очистки, даже если какой-то другой поток обращается к ней в данный момент, является существенным.
Вот метод расширения для создания и добавления в словарь списка самоудаления:
public static class ConcurrentDictionaryExtensions
{
public static IList<TValue> AddSelfRemovingList<TKey, TValue>(this ConcurrentDictionary<TKey, IList<TValue>> dictionaryInstance, TKey key)
{
var newInstance = new SelfRemovingConcurrentList<TKey, TValue>(dictionaryInstance, key);
if (!dictionaryInstance.TryAdd(key, newInstance))
{
throw new ArgumentException("ownerAccessKey", "The passed ownerAccessKey has already exist in the parent dictionary");
}
return newInstance;
}
}
И наконец; Вот синхронизированная, самоудерживающаяся реализация IList<T>
:
public class SelfRemovingConcurrentList<TKey, TValue> : IList<TValue>
{
private ConcurrentDictionary<TKey, IList<TValue>> owner;
private TKey ownerAccessKey;
List<TValue> underlyingList = new List<TValue>();
private bool hasRemovedSelf;
public class HasAlreadyRemovedSelfException : Exception
{
}
internal SelfRemovingConcurrentList(ConcurrentDictionary<TKey, IList<TValue>> owner, TKey ownerAccessKey)
{
this.owner = owner;
this.ownerAccessKey = ownerAccessKey;
}
private void ThrowIfHasAlreadyRemovedSelf()
{
if (hasRemovedSelf)
{
throw new HasAlreadyRemovedSelfException();
}
}
[MethodImpl(MethodImplOptions.Synchronized)]
int IList<TValue>.IndexOf(TValue item)
{
ThrowIfHasAlreadyRemovedSelf();
return underlyingList.IndexOf(item);
}
[MethodImpl(MethodImplOptions.Synchronized)]
void IList<TValue>.Insert(int index, TValue item)
{
ThrowIfHasAlreadyRemovedSelf();
underlyingList.Insert(index, item);
}
[MethodImpl(MethodImplOptions.Synchronized)]
void IList<TValue>.RemoveAt(int index)
{
ThrowIfHasAlreadyRemovedSelf();
underlyingList.RemoveAt(index);
if (underlyingList.Count == 0)
{
hasRemovedSelf = true;
IList<TValue> removedInstance;
if (!owner.TryRemove(ownerAccessKey, out removedInstance))
{
// Just ignore.
// What we want to do is to remove ourself from the owner (concurrent dictionary)
// and it seems like we have already been removed!
}
}
}
TValue IList<TValue>.this[int index]
{
[MethodImpl(MethodImplOptions.Synchronized)]
get
{
ThrowIfHasAlreadyRemovedSelf();
return underlyingList[index];
}
[MethodImpl(MethodImplOptions.Synchronized)]
set
{
ThrowIfHasAlreadyRemovedSelf();
underlyingList[index] = value;
}
}
[MethodImpl(MethodImplOptions.Synchronized)]
void ICollection<TValue>.Add(TValue item)
{
ThrowIfHasAlreadyRemovedSelf();
underlyingList.Add(item);
}
[MethodImpl(MethodImplOptions.Synchronized)]
void ICollection<TValue>.Clear()
{
ThrowIfHasAlreadyRemovedSelf();
underlyingList.Clear();
hasRemovedSelf = true;
IList<TValue> removedInstance;
if (!owner.TryRemove(ownerAccessKey, out removedInstance))
{
// Just ignore.
// What we want to do is to remove ourself from the owner (concurrent dictionary)
// and it seems like we have already been removed!
}
}
[MethodImpl(MethodImplOptions.Synchronized)]
bool ICollection<TValue>.Contains(TValue item)
{
ThrowIfHasAlreadyRemovedSelf();
return underlyingList.Contains(item);
}
[MethodImpl(MethodImplOptions.Synchronized)]
void ICollection<TValue>.CopyTo(TValue[] array, int arrayIndex)
{
ThrowIfHasAlreadyRemovedSelf();
underlyingList.CopyTo(array, arrayIndex);
}
int ICollection<TValue>.Count
{
[MethodImpl(MethodImplOptions.Synchronized)]
get
{
ThrowIfHasAlreadyRemovedSelf();
return underlyingList.Count;
}
}
bool ICollection<TValue>.IsReadOnly
{
[MethodImpl(MethodImplOptions.Synchronized)]
get
{
ThrowIfHasAlreadyRemovedSelf();
return false;
}
}
[MethodImpl(MethodImplOptions.Synchronized)]
bool ICollection<TValue>.Remove(TValue item)
{
ThrowIfHasAlreadyRemovedSelf();
bool removalResult = underlyingList.Remove(item);
if (underlyingList.Count == 0)
{
hasRemovedSelf = true;
IList<TValue> removedInstance;
if (!owner.TryRemove(ownerAccessKey, out removedInstance))
{
// Just ignore.
// What we want to do is to remove ourself from the owner (concurrent dictionary)
// and it seems like we have already been removed!
}
}
return removalResult;
}
[MethodImpl(MethodImplOptions.Synchronized)]
IEnumerator<TValue> IEnumerable<TValue>.GetEnumerator()
{
ThrowIfHasAlreadyRemovedSelf();
return underlyingList.GetEnumerator();
}
[MethodImpl(MethodImplOptions.Synchronized)]
IEnumerator IEnumerable.GetEnumerator()
{
ThrowIfHasAlreadyRemovedSelf();
return underlyingList.GetEnumerator();
}
}