Parallel.ForEach и невозможно прочитать из закрытого исключения TextReader

У меня есть текстовый файл, содержащий различные URL-адреса фидов. Я читаю все URL-адреса в коллекции (IEnumerable), используя следующий код:

var URLs = File.ReadLines(Path.GetFullPath(@"Resources\FeedList.txt"));

В следующей строке я печатаю общее количество:

Console.WriteLine("Total Number of feeds : {0}",URLs.Count());

И после этого я использую конструкцию Parellel.ForEach для выполнения некоторой логики, соответствующей каждому URL-адресу. Ниже приведен код, который я использую:

Parallel.ForEach(URLs, (url) =>
                                       {
                                           // Some business logic
                                       });

Проблема в том, что я получаю следующее исключение, как только я добавляю код для печати количества URL-адресов, то есть кода, который вызывает метод Count() для объекта URL-адресов. Исключение :

Total Number of feeds : 78

Unhandled Exception: System.AggregateException: One or more errors occurred. ---> System.ObjectDisposedException: Cannot read from a closed TextReader.
   at System.IO.__Error.ReaderClosed()
   at System.IO.StreamReader.ReadLine()
   at System.IO.File.<InternalReadLines>d__0.MoveNext()
   at System.Collections.Concurrent.Partitioner.DynamicPartitionerForIEnumerable`1.InternalPartitionEnumerator.GrabNextChunk(Int32 requestedChunkSize)
   at System.Collections.Concurrent.Partitioner.DynamicPartitionEnumerator_Abstract`2.MoveNext()
   at System.Threading.Tasks.Parallel.<>c__DisplayClass32`2.<PartitionerForEachWorker>b__30()
   at System.Threading.Tasks.Task.InnerInvoke()
   at System.Threading.Tasks.Task.InnerInvokeWithArg(Task childTask)
   at System.Threading.Tasks.Task.<>c__DisplayClass7.<ExecuteSelfReplicating>b__6(Object )
   --- End of inner exception stack trace ---
   at System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions)
   at System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout, CancellationToken cancellationToken)
   at System.Threading.Tasks.Parallel.PartitionerForEachWorker[TSource,TLocal](Partitioner`1 source, ParallelOptions parallelOptions, Action`1 simpleBody, Action`2 bodyWi
   at System.Threading.Tasks.Parallel.ForEachWorker[TSource,TLocal](IEnumerable`1 source, ParallelOptions parallelOptions, Action`1 body, Action`2 bodyWithState, Action`3
   at System.Threading.Tasks.Parallel.ForEach[TSource](IEnumerable`1 source, Action`1 body)
   at DiscoveringGroups.Program.Main(String[] args) in C:\Users\Pawan Mishra\Documents\Visual Studio 2010\Projects\ProgrammingCollectiveIntelligence\DiscoveringGroups\Pro
Press any key to continue . . .

И если я удалю/закомментирую строку, которая печатает значение счетчика, цикл Parallel.ForEach будет работать нормально.

Кто-нибудь знает, что здесь происходит не так?


person Pawan Mishra    schedule 31.10.2011    source источник
comment
Что происходит в some business logic?   -  person Jeff Mercado    schedule 31.10.2011


Ответы (1)


Не используйте var, если в этом нет необходимости (или когда тип явно лишний). В этом случае он скрывает то, что происходит, и вы удивляетесь результату.

Метод File.ReadLines не читает все строки и не возвращает коллекцию, он возвращает перечислитель, который считывает строки, когда вы получаете из него элементы. Тип, который он возвращает, является не строковым массивом, а IEnumerable<string>, который вы бы заметили, если бы указали тип для переменной:

string[] URLs = File.ReadLines(Path.GetFullPath(@"Resources\FeedList.txt"));

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

Когда вы используете метод Count() для перечислителя, он прочитает все строки в файле, чтобы подсчитать их, поэтому, когда вы позже попытаетесь снова использовать перечисление, он уже прочитает все строки и закроет TextReader.

Используйте метод File.ReadAllLines для чтения всех строк в файле вместо получения перечислителя:

string[] URLs = File.ReadAllLines(Path.GetFullPath(@"Resources\FeedList.txt"));

Теперь вы можете использовать массив несколько раз.

person Guffa    schedule 31.10.2011
comment
Спасибо. Усвоил урок, используйте ключевое слово var только там, где это необходимо. - person Pawan Mishra; 31.10.2011