Вопрос:

Параллельное вычисление хеша через несколько TransformBlocks приводит к беспорядку

c# multithreading hash

818 просмотра

1 ответ

32 Репутация автора

Я пытаюсь вычислить хэши для всего каталога, чтобы отслеживать изменения позже. Это относительно легко. Однако, если есть большие файлы, вычисления занимают слишком много времени, поэтому я использовал несколько многопоточности.

Благодаря узким местам ввода / вывода я должен прочитать файл с одним потоком, но я могу вычислить хэш для этого файла в нескольких потоках с одновременным вызовом методов TransformBlock. Проблема в том, что результат каждого вычисления различен - потому что все потоки обновляют один экземпляр хэш-алгоритма, они делают это беспорядочно.

  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++;
            }
        }
    }

Есть ли способ организовать процесс хеширования? Я не могу использовать .Net новее 3.5 и такие классы, как BackroundWorker и ThreadPool. Или, может быть, есть другой метод для параллельного вычисления хеша?

Автор: Leeder Источник Размещён: 18.10.2015 09:13

Ответы (1)


2 плюса

67543 Репутация автора

Как правило, вы не можете использовать криптографические объекты в многопоточном коде. Проблема с методами хэширования заключается в том, что они полностью линейны - каждый блок хеширования зависит от текущего состояния, а состояние вычисляется с использованием всех предыдущих блоков. В общем, вы не можете сделать это для MD5.

Существует еще один процесс, который можно использовать, и он называется хеш-деревом или деревом Меркле . В основном вы выбираете размер блока и вычисляете хеш для блоков. Эти хэши объединяются и снова хэшируются. Если у вас очень много хэшей, вы можете создать дерево, как описано в статье в Википедии, на которую мы ссылались ранее. Конечно, полученный хэш отличается от просто MD5 и зависит от параметров конфигурации дерева хешей.

Обратите внимание, что MD5 был сломан. Вы должны использовать SHA-256 или SHA-512 / xxx (быстрее на 64-битных процессорах). Также обратите внимание, что часто скорость ввода-вывода является скорее препятствием, чем скоростью алгоритма хэширования, сводя на нет любые преимущества скорости в деревьях хэширования. Если у вас много файлов, вы также можете распараллелить хеширование на уровне файлов.

Автор: Maarten Bodewes Размещён: 18.10.2015 12:05
Вопросы из категории :
32x32