为什么在使用Parallel.For时会导致ArgumentOutOfRangeException?

时间:2014-08-31 16:09:55

标签: c# multithreading blockingcollection parallel.for

我已经开始编写哈希数字并根据列表检查它们以查看是否存在匹配的哈希。

我使用for循环工作正常,然后我决定使用Parallel.For尝试加速 - 不幸的是,这会导致ArgumentOutOfRangeException我无法调试。

public class HashCompare
{
    private string encryptedCardNumber;
    private Encrypter encrypter;
    private BlockingCollection<string> paraCardNumberList;

    public string Compare(string hash)
    {
        bool exists = Lookup.hashExists(hash);

        if (exists)
        {
            string unencryptedCardNumber = Lookup.GetUnencryptedCardNumber(hash);
            return unencryptedCardNumber;
        }
        return null;
    }

        public BlockingCollection<string> PLCompareAll()
    {

        paraCardNumberList = new BlockingCollection<string>();

        Parallel.For(100000, 999999, i =>               
        {
            encrypter = new Encrypter();

            encryptedCardNumber = encrypter.EncryptCardNumber(i.ToString());
            var result = Compare(encryptedCardNumber);

            if (result != null)
            {
                paraCardNumberList.Add(result);
            }
        });
        paraCardNumberList.CompleteAdding();

        return paraCardNumberList;
    }
}

调用encrypter.EncryptCardNumber时出现错误(貌似在returnValue.ToString()上)

private StringBuilder returnValue

public string EncryptCardNumber(string str)
{
    try
    {
        var sha1 = SHA1.Create();
        byte[] hashData = sha1.ComputeHash(Encoding.Default.GetBytes(str));
        returnValue = new StringBuilder();

        for (int i = 0; i < hashData.Length; i++)
        {
            returnValue.Append(hashData[i].ToString("x2"));
        }
    }
    catch (Exception ex)
    {
        string strerr = "Error in hash code: " + ex.Message;
    }
    return returnValue.ToString();
}

我有两个问题:

  1. 为什么我会例外?
  2. 我是否正确使用BlockingCollection来实现我想要实现的目标?

1 个答案:

答案 0 :(得分:2)

StringBuilder is不是线程安全的:

  

不保证所有实例成员都是线程安全的。

但您似乎在StringBuilder所有EncryptCardNumber来电时都使用相同的.ToString()实例。当一个线程正在执行encrypter而另一个线程正在尝试追加更多字符时,您遇到竞争条件就不足为奇了。

我猜你实际上并不打算让所有这些线程覆盖并附加到彼此的StringBuilder实例上。尝试将对象声明为局部变量,而不是该类的字段。同样的原则也可能适用于其他变量,如BlockingCollection<>

关于你的return Enumerable.Range(100000, 999999) .AsParallel() // One line to make this parallel .Select(i => new Encrypter().EncryptCardNumber(i.ToString()) .Select(Compare) .Where(hash => hash != null) .ToList(); ,这可能是一个很好的方法,但我个人会更有用的东西:

{{1}}