Вы можете реализовать собственную версию IList<T>
, которая обернет базовый List<T>
, чтобы обеспечить блокировку при каждом вызове метода.
public class LockingList<T> : IList<T>
{
public LockingList(IList<T> inner)
{
this.Inner = inner;
}
private readonly object gate = new object();
public IList<T> Inner { get; private set; }
public int IndexOf(T item)
{
lock (gate)
{
return this.Inner.IndexOf(item);
}
}
public void Insert(int index, T item)
{
lock (gate)
{
this.Inner.Insert(index, item);
}
}
public void RemoveAt(int index)
{
lock (gate)
{
this.Inner.RemoveAt(index);
}
}
public T this[int index]
{
get
{
lock (gate)
{
return this.Inner[index];
}
}
set
{
lock (gate)
{
this.Inner[index] = value;
}
}
}
public void Add(T item)
{
lock (gate)
{
this.Inner.Add(item);
}
}
public void Clear()
{
lock (gate)
{
this.Inner.Clear();
}
}
public bool Contains(T item)
{
lock (gate)
{
return this.Inner.Contains(item);
}
}
public void CopyTo(T[] array, int arrayIndex)
{
lock (gate)
{
this.Inner.CopyTo(array, arrayIndex);
}
}
public int Count
{
get
{
lock (gate)
{
return this.Inner.Count;
}
}
}
public bool IsReadOnly
{
get
{
lock (gate)
{
return this.Inner.IsReadOnly;
}
}
}
public bool Remove(T item)
{
lock (gate)
{
return this.Inner.Remove(item);
}
}
public IEnumerator<T> GetEnumerator()
{
lock (gate)
{
return this.Inner.ToArray().AsEnumerable().GetEnumerator();
}
}
IEnumerator IEnumerable.GetEnumerator()
{
lock (gate)
{
return this.Inner.ToArray().GetEnumerator();
}
}
}
Вы бы использовали этот код так:
var list = new LockingList<int>(new List<int>());
Если вы используете большие списки и / или производительность является проблемой, тогда этот тип блокировки может быть не очень эффективным, но в большинстве случаев это должно быть хорошо.
очень важно заметить, что два метода GetEnumerator
вызывают .ToArray()
. Это вызывает оценку перечислителя до снятия блокировки, гарантируя, что любые изменения в списке не влияют на фактическое перечисление.
Использование кода, подобного lock (list) { ... }
или lock (list.SyncRoot) { ... }
, не защищает вас от изменений списка, происходящих во время перечислений. Эти решения покрывают только одновременные изменения в списке - и это только в том случае, если все вызывающие стороны делают это в пределах блокировки. Кроме того, эти решения могут привести к смерти вашего кода, если какой-то неприятный фрагмент кода захватывает блокировку и не снимает ее.
В моем решении вы заметите, что у меня есть object gate
, которая является закрытой переменной внутри класса, который я блокирую. Ничто вне класса не может заблокировать это, так что это безопасно.
Надеюсь, это поможет.