通过多个TransformBlock进行并行哈希计算会导致混乱

我正在尝试为整个目录计算哈希值,以便稍后监视更改。 这相对容易。 但是,如果有大文件,计算需要花费太多时间,所以我最终使用了一些multithreading。

由于I / O瓶颈,我应该读取一个带有一个线程的文件,但我可以一次调用TransformBlock方法在多个线程中计算该文件的哈希值。 问题是,每次计算的结果都不同 – 因为所有线程都更新了hashAlgorithm的一个实例,它们会不规律地执行。

public delegate void CalculateHashDelegate(byte[] buffer); private MD5 md5; private long completed_threads_hash; private object lock_for_hash = new object(); `private string getMd5Hash(string file_path) { string file_to_be_hashed = file_path; byte[] hash; try { CalculateHashDelegate CalculateHash = AsyncCalculateHash; md5 = MD5.Create(); using (Stream input = File.OpenRead(file_to_be_hashed)) { int buffer_size = 0x4096; byte[] buffer = new byte[buffer_size]; long part_count = 0; completed_threads_hash = 0; int bytes_read; while ((bytes_read = input.Read(buffer, 0, buffer.Length)) == buffer_size) { part_count++; IAsyncResult ar_hash = CalculateHash.BeginInvoke(buffer, CalculateHashCallback, CalculateHash); } // Wait for completing all the threads while (true) { lock (completed_threads_lock) { if (completed_threads_hash == part_count) { md5.TransformFinalBlock(buffer, 0, bytes_read); break; } } } hash = md5.Hash; } StringBuilder sb = new StringBuilder(); for (int i = 0; i < hash.Length; i++) { sb.Append(hash[i].ToString("x2")); } md5.Clear(); return sb.ToString(); } catch (Exception ex) { Console.WriteLine("An exception was encountered during hashing file {0}. {1}.", file_to_be_hashed, ex.Message); return ex.Message; } } public void AsyncCalculateHash(byte[] buffer) { lock (lock_for_hash) { md5.TransformBlock(buffer, 0, buffer.Length, null, 0); } } private void CalculateHashCallback(IAsyncResult ar_hash) { try { CalculateHashDelegate CalculateHash = ar_hash.AsyncState as CalculateHashDelegate; CalculateHash.EndInvoke(ar_hash); } catch (Exception ex) { Console.WriteLine("Callback exception: ", ex.Message); } finally { lock (completed_threads_lock) { completed_threads_hash++; } } } 

有没有办法组织散列过程? 我不能使用比3.5更新的.Net以及BackroundWorker和ThreadPool等类。 或许还有另一种并行哈希计算方法?

通常,您不能在multithreading代码中使用加密对象。 散列方法的问题在于它们是完全线性的 – 每个散列块都取决于当前状态,并且使用所有先前的块计算状态。 所以基本上,你不能为MD5做到这一点。

还有另一个可以使用的进程,它被称为哈希树或Merkle树 。 基本上,您决定块大小并计算块的哈希值。 将这些哈希放在一起并再次进行哈希处理。 如果您有大量的哈希值,您实际上可能会创建一个树,如之前链接的维基百科文章中所述。 当然,生成的散列与MD5不同,并且取决于散列树的配置参数。

请注意,MD5已被破坏。 您应该使用SHA-256或SHA-512 / xxx(在64位处理器上更快)。 还要注意,通常IO速度比散列算法的速度更具障碍,否定了散列树的任何速度优势。 如果您有许多文件,还可以在文件级别上并行化哈希。