Как мне контролировать доступ нескольких потоков к коллекции объектов? - PullRequest
2 голосов
/ 26 февраля 2010

Я пишу приложение, которое отображает список объектов, которые пользователь может выбрать, а затем просматривать и редактировать свойства с помощью элемента управления PropertyGrid. Свойства объекта заполняются длительным процессом извлечения информации из файлов с использованием вторичного потока. Но я также хотел бы позволить пользователю продолжить просмотр других объектов в процессе извлечения.

После прочтения ответов на мои предыдущие вопросы по SO. Это похоже на то, что свойства, которые записываются в процессе извлечения, не пересекаются со свойствами, которые могут быть изменены пользователем через сетку свойств. У меня не должно быть проблем с двумя потоками, редактирующими объекты одновременно. Хотя пользователь может увидеть неправильное значение, если ему невероятно не повезло, и сетка свойств в конечном итоге читает объект в середине неатомарной записи.

Однако я все еще хотел бы знать, как я мог бы настроить это, чтобы пользователь не мог редактировать или просматривать объект, который находится в процессе извлечения. Я очень плохо знаком с многопоточностью, но большинство примеров, которые я прочитал, показывают отдельный объект токена, который создается для блокировки доступа к реальному интересующему объекту. Ответы на мой другой предыдущий вопрос подтвердили, что типично создавать отдельный объект, подобный этому, специально для блокировки.

Так что теперь я хотел бы знать, как это обрабатывается в моем случае, когда у меня есть большая коллекция объектов? Я хотел бы создать блокировки, которые не позволяют сетке свойств отображать объект, выбранный пользователем, если он в настоящее время извлекается.

Нужно ли создавать отдельную коллекцию заблокированных объектов, которая будет синхронизирована с моей реальной коллекцией? Итак, если объект добавлен или удален из моей основной коллекции, мне нужно добавить или удалить объекты блокировки из моей коллекции блокировок?

Могу ли я привязываться к реальным объектам, а не создавать отдельные объекты для блокировки токена?

А как насчет добавления булевого свойства IsBeingExtracted к объектам, которые сетка свойств может проверить, чтобы увидеть, находится ли объект в середине записи? Это будет установлено в самом начале и в самом конце процесса извлечения.

Или как насчет статического поля, которое ссылается на текущий (если есть) объект, в который извлекается текущий. Сетка свойств может затем проверить, что последний объект, который он запрашивал для отображения, не был объектом, на который ссылается это статическое поле? Это, конечно, не сработало бы, если было несколько потоков извлечения.

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

Ответы [ 4 ]

1 голос
/ 27 февраля 2010

Не могли бы вы просто сделать коллекцию, которая содержит объекты SynchronizedCollection<T>? Это гарантирует, что только один поток может добавлять или получать доступ к объектам одновременно. Тогда вместо того, чтобы беспокоиться о том, как синхронизировать доступ к свойствам каждого объекта, не добавляйте объект в коллекцию, пока он не будет заполнен.

Примерно так:

private readonly ICollection<Item> Items = new SynchronizedCollection<Item>();

// Run this on the background thread.
public void PopulateItems()
{
    using (var file = File.OpenRead("BigFile.txt"))
    using (var reader = new StreamReader(file))
    {
        while (!reader.EndOfStream)
        {
            var item = new Item();
            PopulateItem(item);
            Items.Add(item);
        }
    }
}

public void PopulateItem(Item item)
{
    // Do time-consuming work.
}

Сетка свойств может с радостью делать с объектом все, что захочет, потому что к тому времени, когда он появится в списке, процесс извлечения завершится с ним. Не требуется явная блокировка.

0 голосов
/ 27 февраля 2010

Я думаю, что лучшим способом было бы использовать объект ManualResetEvent с таймаутом. Таким образом, вы можете сообщить пользователю, что он извлекается, и повторить попытку через несколько секунд.

public class Item : IDisposable // I know it is a horrible class name...
{
    private readonly ManualResetEvent accessibleEvent = new ManualResetEvent(false);

    public void Extract()
    {
        try
        {
            // .....
        }
        finally
        {
            accessibleEvent.Set(); // unlock             
        }
    }

    public void Edit()
    {
        if (!accessibleEvent.WaitOne(1000)) // wait 1 second
        {
            // notify user?    
        }

        // ....
    }

    public void Dispose()
    {
        ((IDisposable)accessibleEvent).Dispose();
    }
}
0 голосов
/ 27 февраля 2010

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

Чем меньше у вас блокировок (скажем, одна глобальная блокировка), тем меньше будет параллелизма, поскольку многие операции могут конкурировать за блокировку и препятствовать запуску друг друга.

Чем больше у вас блокировок, тем больше параллелизма вы можете достичь, но тем больше накладных расходов на управление блокировками.

Использование блокировки, являющейся изменяемым объектом, имеет смысл, если то, что вы защищаете с помощью блокировки, это только этот объект. Вы увидите много примеров использования отдельного объекта, потому что тогда вам не нужно разбираться в объеме защиты.

В вашем конкретном случае я не очень понимаю, что защищается. Вы пытаетесь предотвратить одновременное изменение свойства объекта, то есть фоновым процессом или пользователем? Поскольку пользователь не может одновременно выполнять более одной операции, не нужно много параллелизма, поэтому нет необходимости иметь много блокировок. То, что вы могли бы сделать, - это иметь одну блокировку, которая берется, когда сетка свойств входит в режим редактирования и когда фоновый процесс собирается установить то же свойство для редактирования (в противном случае блокировка не требуется).

0 голосов
/ 26 февраля 2010

Сделайте вашу коллекцию объектов словарём, а затем заблокируйте ключи.

...