关于MemoryStream编写的OutOfMemoryException

时间:2009-11-19 22:39:24

标签: c# stream parallel-processing

我有一些示例应用程序我正在努力尝试获得一些新的.Net 4.0 Parallel Extensions(它们非常好)。我遇到了OutOfMemoryException的(可能真的很愚蠢)问题。我的主要应用程序,我正在寻找插入此示例读取一些数据和大量文件,对它们进行一些处理,然后将它们写出来。我遇到了一些问题,文件变得越来越大(可能是GB)并且担心内存,所以我想并行化导致我走这条道路的事情。

现在,下面的代码在较小的文件上获取OOME,我想我只是遗漏了一些东西。它将读入10-15个文件并很好地将它们写在parellel中,但随后它会在下一个文件中窒息。看起来它的读写速度约为650MB。第二组眼睛将不胜感激。

我正在从FileStream读取MemorySteam,因为这是主应用程序所需要的,我只是想在某种程度上复制它。它从所有类型的地方读取数据和文件,并作为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; }
}
}

2 个答案:

答案 0 :(得分:4)

主逻辑似乎没问题,但是如果在main中的那个空的while循环是文字,那么你正在烧掉不必要的CPU周期。最好更好地使用solTask​​.Wait()。

但是如果单个文件可以以GB为单位运行,那么你仍然存在至少在内存中保存1个的问题,通常是2个(1个正在读取,1个正在处理/写入。

PS1:我刚刚意识到你没有预先分配MemStream。这很糟糕,它必须经常为一个大文件重新调整大小,这会花费大量内存。更好地使用类似的东西:

var ms = new MemoryStream(fs.Length);

然后,对于大文件,您必须考虑大对象堆(LOH)。您确定无法分段处理文件并处理它们吗?

PS2:你不需要构造函数参数的参考,但这不是问题。

答案 1 :(得分:0)

快速查看,在您的ServiceStage.Execute方法中

var ms = new MemoryStream();

我没有看到你在哪里关闭ms或者在使用中。你确实在其他课程中使用。这是一回事。