2009-11-19 2 views
3

У меня есть небольшое приложение-образец. Я работал над попыткой получить некоторые новые параллельные расширения .Net 4.0 (они очень приятные). Я сталкиваюсь с (возможно, очень глупой) проблемой с OutOfMemoryException. Мое главное приложение, которое я ищу, чтобы подключить этот образец к чтению некоторых данных и большого количества файлов, выполняет некоторую обработку на них, а затем записывает их где-то. Я столкнулся с некоторыми проблемами, когда файлы стали больше (возможно, GB) и были обеспокоены памятью, поэтому я хотел распараллелить то, что привело меня по этому пути.OutOfMemoryException в письме MemoryStream

Теперь приведенный ниже код получает OOME на небольших файлах, и я думаю, что я просто что-то пропустил. Он будет читать в 10-15 файлах и записывать их в parellel красиво, но затем он задыхается на следующем. Похоже, что он читается и пишет около 650 МБ. Будет оценен второй набор глаз.

Я читаю в MemorySteam из FileStream, потому что это то, что необходимо для основного приложения, и я просто пытаюсь воспроизвести это до некоторой степени. Он считывает данные и файлы со всех типов мест и работает с ними как с MemoryStreams.

Это использует .Net 4.0 Beta 2, VS 2010.

namespace ParellelJob 
{ 
class Program 
{ 
    BlockingCollection<FileHolder> serviceToSolutionShare; 
    static void Main(string[] args) 
    { 
     Program p = new Program(); 
     p.serviceToSolutionShare = new BlockingCollection<FileHolder>(); 
     ServiceStage svc = new ServiceStage(ref p.serviceToSolutionShare); 
     SolutionStage sol = new SolutionStage(ref p.serviceToSolutionShare); 

     var svcTask = Task.Factory.StartNew(() => svc.Execute()); 
     var solTask = Task.Factory.StartNew(() => sol.Execute()); 

     while (!solTask.IsCompleted) 
     { 

     } 

    } 
} 

class ServiceStage 
{ 
    BlockingCollection<FileHolder> outputCollection; 
    public ServiceStage(ref BlockingCollection<FileHolder> output) 
    { 
     outputCollection = output; 
    } 

    public void Execute() 
    { 
     var di = new DirectoryInfo(@"C:\temp\testfiles"); 
     var files = di.GetFiles(); 
     foreach (FileInfo fi in files) 
     { 
      using (var fs = new FileStream(fi.FullName, FileMode.Open, FileAccess.Read)) 
      { 
       int b; 
       var ms = new MemoryStream(); 
       while ((b = fs.ReadByte()) != -1) 
       { 
        ms.WriteByte((byte)b); //OutOfMemoryException Occurs Here 
       } 
       var f = new FileHolder(); 
       f.filename = fi.Name; 
       f.contents = ms; 

       outputCollection.TryAdd(f); 
      } 
     } 
     outputCollection.CompleteAdding(); 

    } 
} 

class SolutionStage 
{ 
    BlockingCollection<FileHolder> inputCollection; 
    public SolutionStage(ref BlockingCollection<FileHolder> input) 
    { 
     inputCollection = input; 
    } 
    public void Execute() 
    { 
     FileHolder current; 
     while (!inputCollection.IsCompleted) 
     { 
      if (inputCollection.TryTake(out current)) 
      { 
       using (var fs = new FileStream(String.Format(@"c:\temp\parellel\{0}", current.filename), FileMode.OpenOrCreate, FileAccess.Write)) 
       { 
        using (MemoryStream ms = (MemoryStream)current.contents) 
        { 
         ms.WriteTo(fs); 
         current.contents.Close(); 
        } 
       } 
      } 
     } 
    } 
} 

class FileHolder 
{ 
    public string filename { get; set; } 
    public Stream contents { get; set; } 
} 
} 

ответ

4

Основная логика кажется ОК, но если этот пустой цикл в основном является литералом, вы жжете ненужные циклы ЦП. Вместо этого лучше используйте solTask.Wait().

Но если отдельные файлы могут работать в гигабайтах, вы все еще есть проблемы проведения по крайней мере 1 полностью в памяти, и, как правило, 2 (1 читается, 1 обрабатываемый/записи

PS1:. Я просто понял, вы не предварительно выделить MemStream это плохо, то придется заново размер очень часто для большого файла, и что стоит много памяти лучше использовать что-то вроде:..

var ms = new MemoryStream(fs.Length); 

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

PS2: Вам не нужны ссылки ref на параметрах конструктора, но это не проблема.

+0

Предварительная выделение MemoryStream решает мою проблему для всех, кроме самых больших файлов. Я буду разбивать большие файлы на куски. Просто не было этого в этом примере приложения. Благодарю. – MikeD

0

Просто просматривал быстро, внутри метода ServiceStage.Execute у вас есть

var ms = new MemoryStream(); 

Я не вижу, где ты закрывают ms вне или используют его. У вас есть использование в другом классе. Это одна вещь, чтобы проверить.

+1

Я пытаюсь разделить MemoryStream между классами, поместив его в общий BlockingCollection. Поток закрывается после решения SolutionStage с ним. – MikeD

+0

Нет, MemStreams используются для передачи данных и закрываются на принимающей стороне. И, будучи полностью управляемым, вам действительно не нужно Dispose() их. –

Смежные вопросы