需要帮助以使crc计算的类线程安全

时间:2019-09-24 14:20:08

标签: c# task-parallel-library parallel.foreach

当我们在应用程序(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 个答案:

答案 0 :(得分:3)

可能所有的问题都是“静态”功能。 实际上,所有CRC32实例的静态函数都是相同的。 这意味着在实例运行时,设置其参数,另一个实例可以在第一个实例上写入自己的值。