У меня было желание написать что-то для хэш-номеров и проверить их на список, чтобы увидеть, существует ли соответствующий хэш или нет.
Я получил эту работу отлично, используя цикл 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();
}
У меня есть 2 вопроса:
StringBuilder
не является потокобезопасным:
Любые члены экземпляра не гарантируют безопасность потоков.
Но, похоже, вы используете один и тот же экземпляр StringBuilder
для всех ваших вызовов EncryptCardNumber
. Не удивительно, что вы столкнулись с состоянием гонки, когда один поток выполняет .ToString()
а другой пытается добавить больше символов.
Я предполагаю, что вы на самом деле не собираетесь переписывать все эти потоки и добавлять друг к другу экземпляры StringBuilder. Попробуйте объявить объект как локальную переменную, а не поле этого класса. Тот же принцип, вероятно, применим и к другим переменным, таким как encrypter
.
Что касается вашего 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();