Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/324.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# Parallel.ForEach在提取zip文件时引发异常_C#_Foreach_Zip_Task Parallel Library_Parallel.foreach - Fatal编程技术网

C# Parallel.ForEach在提取zip文件时引发异常

C# Parallel.ForEach在提取zip文件时引发异常,c#,foreach,zip,task-parallel-library,parallel.foreach,C#,Foreach,Zip,Task Parallel Library,Parallel.foreach,我正在读取一个zip文件的内容,并试图将其解压缩 var allZipEntries = ZipFile.Open(zipFileFullPath, ZipArchiveMode.Read).Entries; 现在,如果我使用Foreach循环来提取,效果很好。缺点是它相当于zip.extract方法,当我打算提取所有文件时,我没有任何优势 foreach (var currentEntry in allZipEntries) { if (cu

我正在读取一个zip文件的内容,并试图将其解压缩

  var allZipEntries = ZipFile.Open(zipFileFullPath, ZipArchiveMode.Read).Entries;
现在,如果我使用Foreach循环来提取,效果很好。缺点是它相当于zip.extract方法,当我打算提取所有文件时,我没有任何优势

   foreach (var currentEntry in allZipEntries)
        {
            if (currentEntry.FullName.Equals(currentEntry.Name))
            {
                currentEntry.ExtractToFile($"{tempPath}\\{currentEntry.Name}");
            }
            else
            {
                var subDirectoryPath = Path.Combine(tempPath, Path.GetDirectoryName(currentEntry.FullName));
                Directory.CreateDirectory(subDirectoryPath);
                currentEntry.ExtractToFile($"{subDirectoryPath}\\{currentEntry.Name}");
            }

        }
现在,为了利用TPL,尝试使用Parallel.forEach,但这会引发以下异常:

System.IO.Compression.dll中发生“System.IO.InvalidDataException”类型的异常,但未在用户代码中处理

其他信息:本地文件头已损坏

为了避免这种情况,我可以用一把锁,但这完全违背了目的

        Parallel.ForEach(allZipEntries, currentEntry =>
        {
            lock (thisLock)
            {
                if (currentEntry.FullName.Equals(currentEntry.Name))
                {
                    currentEntry.ExtractToFile($"{tempPath}\\{currentEntry.Name}");
                }
                else
                {
                    var subDirectoryPath = Path.Combine(tempPath, Path.GetDirectoryName(currentEntry.FullName));
                    Directory.CreateDirectory(subDirectoryPath);
                    currentEntry.ExtractToFile($"{subDirectoryPath}\\{currentEntry.Name}");
                }
            }

        });

是否有其他或更好的方法提取文件?

并行写入/读取不是一个好主意,因为硬盘驱动器控制器只会逐个运行请求。通过使用多个线程,您只需增加开销并将它们全部排队,而不会获得任何收益

首先尝试将文件读入内存,这将避免异常,但是如果您对其进行基准测试,您可能会发现由于更多线程的开销,其速度实际上较慢

如果文件非常大且解压缩需要很长时间,并行运行解压缩可能会提高速度,但IO读/写不会。无论如何,大多数解压库都已经是多线程的,所以只有当这个库不是多线程的时候,您才能从中获得任何性能增益

编辑:下面是一种使库线程安全的狡猾方法。这取决于zip存档的速度慢/标准差,这证明了这不是从并行性

中获益的东西。
Array.ForEach(Directory.GetFiles(@"c:\temp\output\"), File.Delete);

Stopwatch timer = new Stopwatch();
timer.Start();
int numberOfThreads = 8;
var clonedZipEntries = new List<ReadOnlyCollection<ZipArchiveEntry>>();

for (int i = 0; i < numberOfThreads; i++)
{
    clonedZipEntries.Add(ZipFile.Open(@"c:\temp\temp.zip", ZipArchiveMode.Read).Entries);
}
int totalZipEntries = clonedZipEntries[0].Count;
int numberOfEntriesPerThread = totalZipEntries / numberOfThreads;

Func<object,int> action = (object thread) =>
{
    int threadNumber = (int)thread;
    int startIndex = numberOfEntriesPerThread * threadNumber;
    int endIndex = startIndex + numberOfEntriesPerThread;
    if (endIndex > totalZipEntries) endIndex = totalZipEntries;

    for (int i = startIndex; i < endIndex; i++)
    {
        Console.WriteLine($"Extracting {clonedZipEntries[threadNumber][i].Name} via thread {threadNumber}");
        clonedZipEntries[threadNumber][i].ExtractToFile($@"C:\temp\output\{clonedZipEntries[threadNumber][i].Name}");
    }

    //Check for any remainders due to non evenly divisible size
    if (threadNumber == numberOfThreads - 1 && endIndex < totalZipEntries)
    {
        for (int i = endIndex; i < totalZipEntries; i++)
        {
            Console.WriteLine($"Extracting {clonedZipEntries[threadNumber][i].Name} via thread {threadNumber}");
            clonedZipEntries[threadNumber][i].ExtractToFile($@"C:\temp\output\{clonedZipEntries[threadNumber][i].Name}");
        }
    }
    return 0;
};


//Construct the tasks
var tasks = new List<Task<int>>();
for (int threadNumber = 0; threadNumber < numberOfThreads; threadNumber++) tasks.Add(Task<int>.Factory.StartNew(action, threadNumber));

Task.WaitAll(tasks.ToArray());
timer.Stop();

var threaderTimer = timer.ElapsedMilliseconds;



Array.ForEach(Directory.GetFiles(@"c:\temp\output\"), File.Delete);

timer.Reset();
timer.Start();
var entries = ZipFile.Open(@"c:\temp\temp.zip", ZipArchiveMode.Read).Entries;
foreach (var entry in entries)
{
    Console.WriteLine($"Extracting {entry.Name} via thread 1");
    entry.ExtractToFile($@"C:\temp\output\{entry.Name}");
}
timer.Stop();

Console.WriteLine($"Threaded version took: {threaderTimer} ms");
Console.WriteLine($"Non-Threaded version took: {timer.ElapsedMilliseconds} ms");


Console.ReadLine();

并行写入/读取不是一个好主意,因为硬盘驱动器控制器只能逐个运行请求。通过使用多个线程,您只需增加开销并将它们全部排队,而不会获得任何收益

首先尝试将文件读入内存,这将避免异常,但是如果您对其进行基准测试,您可能会发现由于更多线程的开销,其速度实际上较慢

如果文件非常大且解压缩需要很长时间,并行运行解压缩可能会提高速度,但IO读/写不会。无论如何,大多数解压库都已经是多线程的,所以只有当这个库不是多线程的时候,您才能从中获得任何性能增益

编辑:下面是一种使库线程安全的狡猾方法。这取决于zip存档的速度慢/标准差,这证明了这不是从并行性

中获益的东西。
Array.ForEach(Directory.GetFiles(@"c:\temp\output\"), File.Delete);

Stopwatch timer = new Stopwatch();
timer.Start();
int numberOfThreads = 8;
var clonedZipEntries = new List<ReadOnlyCollection<ZipArchiveEntry>>();

for (int i = 0; i < numberOfThreads; i++)
{
    clonedZipEntries.Add(ZipFile.Open(@"c:\temp\temp.zip", ZipArchiveMode.Read).Entries);
}
int totalZipEntries = clonedZipEntries[0].Count;
int numberOfEntriesPerThread = totalZipEntries / numberOfThreads;

Func<object,int> action = (object thread) =>
{
    int threadNumber = (int)thread;
    int startIndex = numberOfEntriesPerThread * threadNumber;
    int endIndex = startIndex + numberOfEntriesPerThread;
    if (endIndex > totalZipEntries) endIndex = totalZipEntries;

    for (int i = startIndex; i < endIndex; i++)
    {
        Console.WriteLine($"Extracting {clonedZipEntries[threadNumber][i].Name} via thread {threadNumber}");
        clonedZipEntries[threadNumber][i].ExtractToFile($@"C:\temp\output\{clonedZipEntries[threadNumber][i].Name}");
    }

    //Check for any remainders due to non evenly divisible size
    if (threadNumber == numberOfThreads - 1 && endIndex < totalZipEntries)
    {
        for (int i = endIndex; i < totalZipEntries; i++)
        {
            Console.WriteLine($"Extracting {clonedZipEntries[threadNumber][i].Name} via thread {threadNumber}");
            clonedZipEntries[threadNumber][i].ExtractToFile($@"C:\temp\output\{clonedZipEntries[threadNumber][i].Name}");
        }
    }
    return 0;
};


//Construct the tasks
var tasks = new List<Task<int>>();
for (int threadNumber = 0; threadNumber < numberOfThreads; threadNumber++) tasks.Add(Task<int>.Factory.StartNew(action, threadNumber));

Task.WaitAll(tasks.ToArray());
timer.Stop();

var threaderTimer = timer.ElapsedMilliseconds;



Array.ForEach(Directory.GetFiles(@"c:\temp\output\"), File.Delete);

timer.Reset();
timer.Start();
var entries = ZipFile.Open(@"c:\temp\temp.zip", ZipArchiveMode.Read).Entries;
foreach (var entry in entries)
{
    Console.WriteLine($"Extracting {entry.Name} via thread 1");
    entry.ExtractToFile($@"C:\temp\output\{entry.Name}");
}
timer.Stop();

Console.WriteLine($"Threaded version took: {threaderTimer} ms");
Console.WriteLine($"Non-Threaded version took: {timer.ElapsedMilliseconds} ms");


Console.ReadLine();
。页面上不再提到这一点

使用此库无法完成您试图执行的操作。可能还有其他一些库支持每个zip文件有多个线程,但我不希望这样

您可以使用多线程同时解压多个文件,但不能用于同一个zip文件中的多个条目。

。页面上不再提到这一点

使用此库无法完成您试图执行的操作。可能还有其他一些库支持每个zip文件有多个线程,但我不希望这样


您可以使用多线程同时解压多个文件,但不能同时解压同一个zip文件中的多个条目。

问题在于您已读入单个zip文件,并且正在尝试并行解压。你可以做的是将它读入内存,然后并行解压缩,但是在这一行的末尾,当windows访问磁盘时,你的写入IO仍然是非并行的。问题是你在一个zip文件中读取了数据,并且试图并行解压缩。你可以做的是将其读入内存,然后并行提取,但是在这一行的末尾,当windows访问磁盘时,你的写入IO仍然是非并行的。这不是一个答案,非常适合任何其他或更好的方法来提取文件?很确定这解释了一个更好的方法,为什么。怎么解释?我已经提到了三种有问题的方法,你回答说评论提到了其中一种,但对任何一种方法都不清楚。它与上述三种方法有何不同和更好?您的方法存在缺陷,并且由于上述原因无法工作。您认为缺陷是什么意思?你看过问题了吗?有3种方法,我不知道第一种和第三种方法如何不起作用。你能写几行来说明你所说的“不同”是什么意思吗?这不是一个答案,也不适合任何其他或更好的方法来提取文件?很确定这解释了一个更好的方法,为什么。怎么解释?我已经提到了三种有问题的方法,你回答说评论提到了其中一种,但对任何一种方法都不清楚。它与上述三种方法有何不同和更好?您的方法存在缺陷,并且由于上述原因无法工作。您认为缺陷是什么意思?你看过问题了吗?有3种方法,我不知道第一和第三种方法如何
我不工作。你能写几行来说明你所说的不同是什么意思吗?然后你需要多个ZipFile实例。应该可以,因为它只是在读取zip。。虽然我同意ZipFile不是线程安全的,但链接页面不包含任何对线程安全的引用。@Stuart Axon:t应该可以,因为它只是在读取zip。-那不是我最近的经历;即使并行地从ZipArchive中读取也会抛出错误。@MitchWheat,看起来页面已经更新了。它肯定是这么说的:然后需要多个ZipFile实例。应该可以,因为它只是在读取zip。。虽然我同意ZipFile不是线程安全的,但链接页面不包含任何对线程安全的引用。@Stuart Axon:t应该可以,因为它只是在读取zip。-那不是我最近的经历;即使并行地从ZipArchive中读取也会抛出错误。@MitchWheat,看起来页面已经更新了。它过去肯定这么说: