У меня есть класс (упрощенный пример) вроде:
public class SomeCollection : ICloneable
{
public void Add(Item item) { /* ... */ }
public void Remove(Item item) { /* ... */ }
public Item Get(Key key) { /* ... */ }
/*
...
*/
public object Clone() { /* ... */ }
}
Мне нужно, чтобы когда поток входил в Clone (), другой поток не мог войти в Add или Remove, но мог войти в Get. Сначала я подумал:
public void Add(Item item) { lock(addLock) { /* ... */ } }
public void Remove(Item item) { lock(removeLock) { /* ... */ } }
public object Clone(Item item)
{
lock(addLock)
{
lock(removeLock)
{
/* ... */
}
}
}
Это работает (я думаю), но имеет определенные недостатки:
* Я не хочу, чтобы два потока входили в Add, чтобы блокировать друг друга - я имею дело с этим глубже в коде
* Мне придется выдерживать накладные расходы блокировки для каждого вызова Add или Remove
Тогда я подумал об этом
private volatile bool cloning = false; // notice the volatile keyword
public void Add(Item item)
{
int i = 0;
while(cloning)
{
if (i++ > 20)
throw new TimeoutException();
Thread.Sleep(50); // waits 50 milliseconds
}
/* ... */
} // and the same for Remove
public object Clone()
{
cloning = true;
try
{
/* do the cloning */
} finally { cloning = false; }
}
Однако этот подход:
- Сложнее
- Клон может войти, пока поток не завершил выполнение операции добавления или удаления
- кажется неестественным
Я коротко посмотрел ReadWriterLockSlim , но, похоже, не подходит к моему сценарию.
Мне это нужно, потому что метод Clone занимает много времени (может занять больше секунды - коллекция ОГРОМНА), и изменение в этом времени взорвет счетчик (используемый в цикле foreach). Да, я должен использовать foreach, поскольку основная коллекция ключей не предоставляет ничего, кроме IEnumerable.
Что за вы рекомендуете?