2009-11-19 5 views
3

J'ai une petite application d'exemple que je travaillais à essayer d'obtenir quelques-uns des nouveaux .Net 4.0 extensions parallèles vont (ils sont très gentils). Je cours dans un problème (probablement vraiment stupide) avec une OutOfMemoryException. Mon application principale que je cherche à brancher cet échantillon dans des lectures de certaines données et beaucoup de fichiers, fait un peu de traitement sur eux, puis les écrit quelque part. Je rencontrais quelques problèmes avec les fichiers qui grossissaient (peut-être GB's) et j'étais préoccupé par la mémoire, donc je voulais paralléliser les choses qui m'ont conduit dans cette voie.OutOfMemoryException sur MemoryStream écrit

Maintenant, le code ci-dessous obtient un oome sur des fichiers plus petits et je pense que je suis juste manque quelque chose. Il va lire dans 10-15 fichiers et les écrire en parellel bien, mais alors il étouffe sur le prochain. On dirait qu'il est lu et écrit sur 650Mo. Un deuxième ensemble d'yeux serait apprécié.

Je lis un MemorySteam à partir du FileStream parce que c'est ce qui est nécessaire pour l'application principale et j'essaie juste de le répliquer dans une certaine mesure. Il lit les données et les fichiers de tous les types d'endroits et travaille sur eux comme MemoryStreams.

Ce utilise .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; } 
} 
} 

Répondre

4

La logique principale semble correcte, mais si cette boucle while vide dans main est littérale, alors vous brûlez des cycles CPU inutiles. Mieux vaut utiliser solTask.Wait() à la place.

Mais si les fichiers individuels peuvent fonctionner en giga-octets, vous avez encore le problème de la tenue d'au moins 1 complètement en mémoire, et le plus souvent 2 (1 en cours de lecture, 1 en cours de traitement/écrit

PS1. Je viens de réaliser vous ne pré-allouer les MemStream C'est mauvais, il faudra redimensionner très souvent pour un gros fichier, et qui coûte beaucoup de mémoire Mieux utiliser quelque chose comme:..

var ms = new MemoryStream(fs.Length); 

et puis, pour les gros fichiers, vous devez considérer le gros tas d'objets (LOH). Êtes-vous sûr de ne pas pouvoir décomposer un fichier en segments et le traiter?

PS2: Et vous n'avez pas besoin des références sur les paramètres du constructeur, mais ce n'est pas le problème.

+0

pré-allocation du MemoryStream résout mon problème pour tous les fichiers sauf les plus volumineux. Je vais diviser des fichiers plus gros en morceaux. Je n'avais pas cela dans cet exemple d'application. Merci. – MikeD

0

Il suffit de regarder à travers rapidement, à l'intérieur de votre méthode de ServiceStage.Execute vous avez

var ms = new MemoryStream(); 

Je ne vois pas où vous ferment ms ou l'utilisent. Vous avez l'utilisation dans l'autre classe. C'est une chose à vérifier.

+1

J'essaie de partager le MemoryStream entre les classes en le plaçant dans le BlockingCollection partagé. Le flux se ferme après que le SolutionStage soit terminé avec. – MikeD

+0

Non, les flux MemStreams sont utilisés pour transférer les données et sont fermés à la réception. Et étant totalement géré, vous n'avez pas vraiment besoin de les détruire. –