There is a large file. The file is divided into blocks of a given length. For each block, the hash function SHA256 is calculated. Calculations should be performed using multi-threading with maximum system load. Each calculated block in size may exceed the RAM of the computer.
As I see the solution to this problem. I have a thread that reads data from a file. Because it is impossible to read the whole block at once, due to the lack of memory, I divide it into several parts and write it into a queue of limited size. I also have several threads that take these parts of the block and calculate the intermediate hash value using the TransformBlock method of the SHA256Managed class. After all the parts of the block are calculated, we call TransformFinalBlock and get the hash of the block.
The problem is that, as I understand it, this method requires strict adherence to the order of the calculated parts of the block, otherwise the resulting value will be different. Actually I don’t know how to provide it. The streams calculate parts of the block in parallel and return the result in a different order.
How to synchronize threads so that they produce results in the right order?
Code running inside threads:
public class CalculatingSha256 { private static object lockObj = new object(); private static SortedList sha256DataList; public CalculatingSha256() { sha256DataList = new SortedList(); sha256DataList = SortedList.Synchronized(sha256DataList); } public void CalculateSha256(PartBlock block) { int index = sha256DataList.IndexOfKey(block.IdFullBlock); SHA256Data sha256Data = null; if (index >= 0) { sha256Data = (SHA256Data)sha256DataList.GetByIndex(index); } if (sha256Data == null) { sha256Data = new SHA256Data(); if (block.SizeFullBlock != 0) { sha256Data.BlockSize = block.SizeFullBlock; } sha256Data.Sha256 = new SHA256Managed(); lock (lockObj) { sha256DataList.Add(block.IdFullBlock, sha256Data); } } Console.WriteLine(block.Id+" "+block.SizeFullBlock+" "+block.IdFullBlock); sha256Data.Sha256.TransformBlock(block.Data, 0, block.Size, block.Data, 0); sha256Data.CurrentCalculated += block.Size; bool bBlockCalculateIsOver = sha256Data.CurrentCalculated == sha256Data.BlockSize; if ((sha256Data.BlockSize != 0) && (bBlockCalculateIsOver)) { byte[] input = new byte[0]; sha256Data.Sha256.TransformFinalBlock(input, 0, 0); string hashString = ToString(sha256Data.Sha256.Hash); Console.WriteLine("Блок номер {0}:{1}", block.IdFullBlock+1, hashString.ToUpper()); sha256DataList.Remove(block.IdFullBlock); } } } public abstract class Block { public int Id { get; set; } } public class PartBlock : Block { public int Size { get; set; } public byte[] Data { get; set; } //номер целого блока которому принадлежит public int IdFullBlock { get; set; } public int SizeFullBlock { get; set; } } public class SHA256Data { public int BlockSize { get; set; } public int CurrentCalculated { get; set; } public SHA256Managed Sha256 { get; set; } }