Как безопасно вызвать Count (Func) в IEnumerable, который может быть изменен другим потоком? - PullRequest
1 голос
/ 28 июня 2011

Один из моих элементов WPF связан со свойством, которое вызывает Count(Func) для IEnumerable. Свойство отображает что-то вроде количества активных объектов в системе (таким образом, требуется параметр Func).

public int ActiveEntitiesCount
{
    get
    {
        return Entities.Count((item) =>
        {
            //my own code, just a couple of value comparisons, very quick
        });
    }
}

. Однако IEnumerable - это список, который может быть изменен другим потоком в любое время. Иногда происходит сбой приложения, по-видимому, из-за изменения IEnumerable во время выполнения перечисления функции Count.

Я могу поставить блок try-catch, но как получить значение, возвращаемое этим свойством?

Примечание: использование блокировки может быть непрактичным, потому что я не знаю всех кодов, которые обращаются к IEnumerable

Ответы [ 3 ]

3 голосов
/ 28 июня 2011

Вы можете решить эту проблему, сделав снимок своего списка перед запуском Count.Например:

return Entities.ToList().Count(item => ...

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

[ПРАВИТЬ] Возможное решение:

public class MyCollection<T> : Collection<T>
{
    private readonly object syncRoot = new object();

    protected override void SetItem(int index, T item)
    {
        lock (syncRoot)
            base.SetItem(index, item);
    }

    protected override void InsertItem(int index, T item)
    {
        lock (syncRoot)
            base.InsertItem(index, item);
    }

    protected override void ClearItems()
    {
        lock (syncRoot)
            base.ClearItems();
    }

    protected override void RemoveItem(int index)
    {
        lock (syncRoot)
            base.RemoveItem(index);
    }

    public new int Count(Func<T, bool> predicate)
    {
        lock (syncRoot)
            return Enumerable.Count(this, predicate);
    }
}
3 голосов
/ 28 июня 2011

Вам понадобится какой-то способ управления параллелизмом, или с предоставлением снимка, или с использованием безопасной для параллелизма коллекции (как в System.Collections.Concurrent). Повторная попытка операции, перехват исключения и повторная попытка, вероятно, сработают, но это довольно ужасно.

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

Если бы вы могли рассказать нам больше о контексте, мы могли бы помочь больше.

0 голосов
/ 28 июня 2011

Вы используете IEnumerable способом, который не поддерживается. Предполагается, что источник перечисления не изменяется в течение времени жизни, когда перечисление «открыто» или используется. Период.

Одним из решений было бы требование, чтобы все потребители IEnumeration и источник данных, из которого они получены, взяли блокировку мьютекса перед тем, как получить доступ к данным по любой причине. Тяжелый и шумный.

Другим решением было бы сохранить собственное свойство Count, которое обновляется (потокобезопасным способом) при каждом изменении источника данных. Таким образом, вам не нужен IEnumerable.Count ().

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...