为什么在使用 Parallel.For 时会导致 ArgumentOutOfRangeException? [英] Why is this causing an ArgumentOutOfRangeException when using Parallel.For?

查看:69
本文介绍了为什么在使用 Parallel.For 时会导致 ArgumentOutOfRangeException?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我尝试编写一些东西来散列数字,并根据列表检查它们以查看是否存在匹配的散列.

I have had a go at writing something to hash numbers and check them against a list to see if a matching hash exists or not.

我使用 for 循环使这个工作正常,然后我决定尝试使用 Parallel.For 加快速度 - 不幸的是,这会导致我在调试时遇到问题的 ArgumentOutOfRangeException.

I got this working fine using a for loop, I then decided to try speed things up by using Parallel.For - unfortunately this causes an ArgumentOutOfRangeException that i'm having trouble debugging.

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())

The error occurs randomly when calling encrypter.EncryptCardNumber (seemingly on the 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 来实现我想要实现的目标是否正确?

推荐答案

StringBuilder 不是线程安全的:

不保证任何实例成员都是线程安全的.

Any instance members are not guaranteed to be thread safe.

但您似乎对所有 EncryptCardNumber 调用使用了相同的 StringBuilder 实例.当一个线程正在执行 .ToString() 而另一个线程正在尝试附加更多字符时,您会遇到竞争条件并不奇怪.

But it appears you're using the same instance of StringBuilder for all of your EncryptCardNumber calls. It's not surprising that you'd encounter a race condition when one thread is doing a .ToString() while another is trying to append more characters.

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

I'm guessing that you don't actually intend to have all of these threads overwriting and appending to each other's instances of StringBuilder. Try declaring the object as a local variable, rather than a field on that class. The same principle probably also applies to other variables, like encrypter.

至于您的 BlockingCollection<>,这可能是一个不错的方法,但我个人会采用更实用的方法:

As for your BlockingCollection<>, that's probably a fine approach, but I'd personally go with something a little more functional:

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();

这篇关于为什么在使用 Parallel.For 时会导致 ArgumentOutOfRangeException?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆