Observable.Using和异步流导致数据损坏

时间:2016-11-25 13:45:29

标签: c# .net system.reactive reactive-programming ziparchive

我有一组流,其目标是计算一组.zip文件中内容的简单&#34;校验和&#34; < / p>

为此,我设置了一个观察点:

  1. 获取给定文件夹中的所有文件
  2. 读取每个文件的内容(读作ZipArchive
  3. 对于每个文件中的每个条目,执行校验和
  4. 的计算

    为了说明这一点,我创建了这个例子:

    注意使用AsyncContext.Runhttps://stackoverflow.com/a/9212343/1025407)使Main方法等待GetChecksum ,因为它是控制台应用< / p>

    namespace DisposePoC
    {
        using System.Collections.Generic;
        using System.IO;
        using System.IO.Compression;
        using System.Reactive.Linq;
        using Nito.AsyncEx;
        using System.Linq;
        using System.Threading.Tasks;
    
    
        class Program
        {
            private static void Main()
            {
                AsyncContext.Run(GetChecksums);
            }
    
            private static async Task<IList<byte>> GetChecksums()
            {
                var bytes = Directory.EnumerateFiles("FolderWithZips")
                    .ToObservable()
                    .SelectMany(path => Observable.Using(() => CreateZipArchive(path), archive => archive.Entries.ToObservable()))
                    .SelectMany(entry => Observable.Using(entry.Open, stream => Observable.FromAsync(() => CalculateChecksum(stream, entry.Length))));
    
                return await bytes.ToList();
            }
    
            private static ZipArchive CreateZipArchive(string path)
            {
                return new ZipArchive(new FileStream(path, FileMode.Open, FileAccess.Read));
            }
    
            private static async Task<byte> CalculateChecksum(Stream stream, long entryLength)
            {
                var bytes = await GetBytesFromStream(stream, entryLength);
                return bytes.Aggregate((b1, b2) => (byte) (b1 ^ b2));
            }
    
            private static async Task<byte[]> GetBytesFromStream(Stream stream, long entryLength)
            {
                byte[] bytes = new byte[entryLength];
                await stream.ReadAsync(bytes, 0, (int)entryLength);
                return bytes;            
            }
        }
    }
    

    运行应用程序,我遇到各种错误:

      

    &#39; System.IO.InvalidDataException&#39;:本地文件头已损坏。   &#39; System.NotSupportedException&#39 ;: Stream不支持阅读。   &#39; System.ObjectDisposedException&#39; :无法访问已处置的对象。   &#39; System.IO.InvalidDataException&#39; :块长度与其补码不匹配。

    我做错了什么?

    observable本身是否存在问题,还是因为ZipArchive不是线程安全的?如果不是,我该如何使代码工作?

2 个答案:

答案 0 :(得分:2)

Rx可能不是最合适的。老实说,你甚至可以在没有异步的情况下做到这一点。

Directory.EnumerateFiles("FolderWithZips")
         .AsParallel()
         .Select(folder => CalculateChecksum(folder))
         .ToList()

答案 1 :(得分:1)

似乎没有关于你的问题的“Rx”。

如果你将整个事物修改成一组命令式的循环,那就可以了。

private static async Task<IList<byte>> GetChecksums()
{
    var bytes = new List<byte>();
    foreach (var path in Directory.EnumerateFiles("FolderWithZips"))
    {
        using (var archive = CreateZipArchive(path))
        {
            foreach (var entry in archive.Entries)
            {
                using (var stream = entry.Open())
                {
                    var checksum = await CalculateChecksum(stream, entry.Length);
                    bytes.Add(checksum);
                }
            }
        }
    }

    return bytes;
}

所以我想你会遇到一系列竞争条件(并发)和/或无序处理问题。