Общие IEnumerable‹T› и IQueryable‹T› в многопоточном приложении

У меня мало сомнений относительно того, как осуществляется доступ к общим 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().


person Nawaz    schedule 15.04.2011    source источник


Ответы (4)


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

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

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

person Thomas Levesque    schedule 15.04.2011

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

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

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

person Tejs    schedule 15.04.2011
comment
Это по-прежнему будет зависеть от потокобезопасности SessionFile и Pattern. - person sehe; 15.04.2011

Посмотрите на Parallel.For и друзей

http://msdn.microsoft.com/en-us/library/dd460693.aspx

person sehe    schedule 15.04.2011

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

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

person BlueRaja - Danny Pflughoeft    schedule 15.04.2011