Совместно используемые IEnumerable <T>и IQueryable <T>в многопоточном приложении - PullRequest
5 голосов
/ 15 апреля 2011

У меня мало сомнений относительно того, как совместно используемые IEnumerable и IQueryable доступны в многопоточном приложении.

Рассмотрим этот фрагмент кода.

ObservableCollection<SessionFile> files = /* some code */
IEnumerable<Pattern> allFilePatterns= /*some query */

foreach (Pattern pattern in allFilePatterns)
{
   string iclFilePath = Path.Combine(pattern.Location, pattern.Filename);
   SessionFile sfile = new SessionFile(iclFilePath, pattern.AnalysisDate);

   SomeDelegate invoker = new SomeDelegate(sfile.SomeHandler);
   invoker.BeginInvoke(allFilePatterns, null, null);

   files.Add(sfile );
}

Как видите, я использую BeginInvoke(), передавая один и тот же экземпляр allFilePatterns каждому обработчику с именем sfile.SomeHandler.

Предположим, что в SomeHandler я повторяю allFilePatterns в цикле foreach, что-то вроде этого:

void SomeHandler(IEnumerable<Pattern> allFilePatterns)
{
    foreach(Pattern pattern in allFilePatterns)
    {
          //some code
    }
}

Теперь я сомневаюсь, что: поскольку BeginInvoke() является асинхронным, это означает, что все foreach во всех SomeHandler всех файлов будут выполняться параллельно (каждый в своем собственном потоке), будет ли общий экземпляр IEnumerable перечислить как ожидалось / нормально? Это правильный подход? Можно ли совместно использовать один и тот же экземпляр IEnumerable в нескольких потоках и параллельно перечислять его?

А что если я использую IQueryable вместо IEnumerable в приведенном выше коде? Любой побочный эффект, о котором я должен знать?

Если это не потокобезопасно, то что мне использовать?

Обратите внимание, что я использую IQueryable для запросов к базе данных, так как я не хочу извлекать все данные из базы данных. Поэтому я хочу максимально избежать IQueryable.ToList().

Ответы [ 4 ]

5 голосов
/ 15 апреля 2011

Это зависит от реализации. Некоторые реализации IEnumerable<T> также реализуют IEnumerator<T> и возвращают себя из GetEnumerator(). В этом случае это, очевидно, не потокобезопасный ...

Что касается IQueryable<T>, это также зависит от реализации. Например, контексты Entity Framework не являются поточно-ориентированными и будут правильно работать только в том потоке, который их создал.

Так что нет уникального ответа на этот вопрос ... он, вероятно, будет работать для некоторых реализаций, а не для других.

1 голос
/ 15 апреля 2011

Я бы ToList() перечислил ваш аргумент при передаче делегату в качестве аргумента фактического создания нового набора для работы потока и избежания проблем.

Однако мне интересно, зачем вам нужно, чтобы каждый элемент перечислимого перечислялся N раз (фактически N ^ 2)? Это звучит неэффективно.

РЕДАКТИРОВАТЬ: Обновлено с моим намерением

0 голосов
/ 15 апреля 2011

Хорошо, одна вещь, которую вы могли бы сделать, если работать с IQueryable, который не является поточно-ориентированным (например, запрос Entity Framework), - это перечислить результаты в одном потоке, но затем передать результаты новые темы по мере необходимости.

Тогда не имеет значения, является ли IQueryable / IEnumerable поточно-ориентированным.

0 голосов
/ 15 апреля 2011
...