нужна помощь, чтобы сделать поток класса безопасности CRC поток - PullRequest
1 голос
/ 24 сентября 2019

Когда мы добавили параллизм в нашем приложении (сервис dotnet), мы обнаружили неожиданное поведение при вычислении crc над текстовыми документами.чтобы изолировать проблему, я создал контрольный пример.Расчет CRC не выполняется при вызове из параллельного цикла.в этом тестовом случае замена параллельного foreach на стандартный всегда прекрасна.Я думаю, что я сделал такое изменение в реализации класса crc32, но мне нужна помощь, чтобы понять правильный путь.Спасибо.

это метод испытания.

[TestMethod()]
public void Test_Crc_TestoDoc()
{
 string query = @"select top 100 docId from sometable";
 ///key is document's id
 ///value is a couple, crc and text
 Dictionary<int, Tuple<int, string>> docs = new Dictionary<int, Tuple<int, string>>();
 using (SqlDataReader oSqlDataReader = Utility.ExecuteSP_Reader(query))
 {
  while (oSqlDataReader.Read())
  {
   int docId = oSqlDataReader.GetInt32(0);
   ///retrive the text by docId
   string docText = Utility.GetDocText(docId);
   ///calculate and add crc in dic
   int CRC = CRC32.Compute(docText);
   docs.Add(docId, new Tuple<int, string>(CRC, docText));
  }
  oSqlDataReader.Close();
 }
 ///calculate crc 100 times to check if the value
 ///is always the same for same text
 for (int i = 0; i < 100; i++)
 {
   Parallel.ForEach(docs.Keys,(int docId) =>
   {
     ///crc saved in dictionary
     int CRC1 = docs[docId].Item1;
     ///text saved in dictionary
     string docText = docs[docId].Item2;
     ///calculate crc again, crc2 must be equal to crc1 stored in dictionary
     int CRC2 = CRC32.Compute(docText);
               Assert.AreEqual(CRC1, CRC2, $"crc not equal, why? docId->{docId} CRC1->{CRC1} CRC2->{CRC2}");
           });
        }
    }

crc32 класс:

public class CRC32 : HashAlgorithm
{

    #region CONSTRUCTORS
    /// <summary>Creates a CRC32 object using the <see cref="DefaultPolynomial"/>.</summary>
    public CRC32()
        : this(DefaultPolynomial)
    {
    }

    /// <summary>Creates a CRC32 object using the specified polynomial.</summary>
    /// <remarks>The polynomical should be supplied in its bit-reflected form. <see cref="DefaultPolynomial"/>.</remarks>
    [CLSCompliant(false)]
    public CRC32(uint polynomial)
    {
        HashSizeValue = 32;
        _crc32Table = (uint[])_crc32TablesCache[polynomial];
        if (_crc32Table == null)
        {
            _crc32Table = CRC32._buildCRC32Table(polynomial);
            _crc32TablesCache.Add(polynomial, _crc32Table);
        }
        Initialize();
    }

    // static constructor
    static CRC32()
    {
        _crc32TablesCache = Hashtable.Synchronized(new Hashtable());
        _defaultCRC = new CRC32();
    }
    #endregion

    #region PROPERTIES
    /// <summary>Gets the default polynomial (used in WinZip, Ethernet, etc.)</summary>
    /// <remarks>The default polynomial is a bit-reflected version of the standard polynomial 0x04C11DB7 used by WinZip, Ethernet, etc.</remarks>
    [CLSCompliant(false)]
    public static readonly uint DefaultPolynomial = 0xEDB88320; // Bitwise reflection of 0x04C11DB7;
    #endregion

    #region METHODS
    /// <summary>Initializes an implementation of HashAlgorithm.</summary>
    public override void Initialize()
    {
        _crc = _allOnes;
    }

    /// <summary>Routes data written to the object into the hash algorithm for computing the hash.</summary>
    protected override void HashCore(byte[] buffer, int offset, int count)
    {
        for (int i = offset; i < count; i++)
        {
            ulong ptr = (_crc & 0xFF) ^ buffer[i];
            _crc >>= 8;
            _crc ^= _crc32Table[ptr];
        }
    }

    /// <summary>Finalizes the hash computation after the last data is processed by the cryptographic stream object.</summary>
    protected override byte[] HashFinal()
    {
        byte[] finalHash = new byte[4];
        ulong finalCRC = _crc ^ _allOnes;

        finalHash[0] = (byte)((finalCRC >> 0) & 0xFF);
        finalHash[1] = (byte)((finalCRC >> 8) & 0xFF);
        finalHash[2] = (byte)((finalCRC >> 16) & 0xFF);
        finalHash[3] = (byte)((finalCRC >> 24) & 0xFF);

        return finalHash;
    }

    /// <summary>Computes the CRC32 value for the given ASCII string using the <see cref="DefaultPolynomial"/>.</summary>
    public static int Compute(string asciiString)
    {
        _defaultCRC.Initialize();
        return ToInt32(_defaultCRC.ComputeHash(asciiString));
    }

    /// <summary>Computes the CRC32 value for the given input stream using the <see cref="DefaultPolynomial"/>.</summary>
    public static int Compute(Stream inputStream)
    {
        _defaultCRC.Initialize();
        return ToInt32(_defaultCRC.ComputeHash(inputStream));
    }

    /// <summary>Computes the CRC32 value for the input data using the <see cref="DefaultPolynomial"/>.</summary>
    public static int Compute(byte[] buffer)
    {
        _defaultCRC.Initialize();
        return ToInt32(_defaultCRC.ComputeHash(buffer));
    }

    /// <summary>Computes the hash value for the input data using the <see cref="DefaultPolynomial"/>.</summary>
    public static int Compute(byte[] buffer, int offset, int count)
    {
        _defaultCRC.Initialize();
        return ToInt32(_defaultCRC.ComputeHash(buffer, offset, count));
    }

    /// <summary>Computes the hash value for the given ASCII string.</summary>
    /// <remarks>The computation preserves the internal state between the calls, so it can be used for computation of a stream data.</remarks>
    public byte[] ComputeHash(string asciiString)
    {
        byte[] rawBytes = ASCIIEncoding.ASCII.GetBytes(asciiString);
        return ComputeHash(rawBytes);
    }

    /// <summary>Computes the hash value for the given input stream.</summary>
    /// <remarks>The computation preserves the internal state between the calls, so it can be used for computation of a stream data.</remarks>
    new public byte[] ComputeHash(Stream inputStream)
    {
        byte[] buffer = new byte[4096];
        int bytesRead;
        while ((bytesRead = inputStream.Read(buffer, 0, 4096)) > 0)
        {
            HashCore(buffer, 0, bytesRead);
        }
        return HashFinal();
    }

    /// <summary>Computes the hash value for the input data.</summary>
    /// <remarks>The computation preserves the internal state between the calls, so it can be used for computation of a stream data.</remarks>
    new public byte[] ComputeHash(byte[] buffer)
    {
        return ComputeHash(buffer, 0, buffer.Length);
    }

    /// <summary>Computes the hash value for the input data.</summary>
    /// <remarks>The computation preserves the internal state between the calls, so it can be used for computation of a stream data.</remarks>
    new public byte[] ComputeHash(byte[] buffer, int offset, int count)
    {
        HashCore(buffer, offset, count);
        return HashFinal();
    }
    #endregion

    #region PRIVATE SECTION
    private static uint _allOnes = 0xffffffff;
    private static CRC32 _defaultCRC;
    private static Hashtable _crc32TablesCache;
    private uint[] _crc32Table;
    private uint _crc;

    // Builds a crc32 table given a polynomial
    private static uint[] _buildCRC32Table(uint polynomial)
    {
        uint crc;
        uint[] table = new uint[256];

        // 256 values representing ASCII character codes. 
        for (int i = 0; i < 256; i++)
        {
            crc = (uint)i;
            for (int j = 8; j > 0; j--)
            {
                if ((crc & 1) == 1)
                    crc = (crc >> 1) ^ polynomial;
                else
                    crc >>= 1;
            }
            table[i] = crc;
        }

        return table;
    }

    private static int ToInt32(byte[] buffer)
    {
        return BitConverter.ToInt32(buffer, 0);
    }
    #endregion

}

1 Ответ

3 голосов
/ 24 сентября 2019

Вероятно, проблема заключается во «статической» функции.Фактически статическая функция одинакова для всех экземпляров CRC32.Это означает, что во время работы экземпляра, устанавливающего свой параметр, другой может записать собственное значение поверх первого.

...