Блокировка нескольких методов, только если поток находится в определенном методе - PullRequest
2 голосов
/ 18 июня 2009

У меня есть класс (упрощенный пример) вроде:

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.

Что за вы рекомендуете?

1 Ответ

1 голос
/ 18 июня 2009

Не могли бы вы объяснить, почему ReaderWriterLockSlim не подходит для вашего сценария?

Конечно, вы не будете использовать его для классического использования, но рассматривайте метод Clone как своего автора и методы добавления / удаления как своих читателей, и я думаю, что он подходит.

[править] Еще одна вещь: если вы идете по этому пути, убедитесь, что вы задокументировали, почему вы используете ReaderWriterLockSlim «назад», чтобы следующий человек, который читает код (или вы через шесть месяцев), понимал, что происходит.

...