嘗試運行Parallel.ForEach以從外部庫Z4DLL_NET查找結果。 dll的文檔說這種類型是多線程安全的。我們有一個龐大的數據集,我們每個月都在進行地址驗證。Parallel.ForEach上的AccessViolationException
當運行任何大於1的批處理大小時,我在Lookup中的_accumail.Lookup()上出現訪問衝突異常錯誤。
我試圖通過使用MaxDegreeOfParallelism來減少線程的數量,但它並沒有阻止這個問題。任何想法將不勝感激。
Web服務代碼:
public void ProcessByBatchId(int batchId, int batchSize)
{
// get addresses to process
var allAddresses = GetAddresses(batchId);
var count = 0;
// get initial set of addresses to process
var addresses = ParseAddresses(allAddresses, count, batchSize).ToList();
while (addresses.Any())
{
count += addresses.Count();
// connect to db
using (var entities = new Entities())
{
// turn these options off since they aren't needed here
entities.Configuration.AutoDetectChangesEnabled = false;
entities.Configuration.ValidateOnSaveEnabled = false;
entities.Configuration.ProxyCreationEnabled = false;
entities.Configuration.LazyLoadingEnabled = false;
// process each address in parallel
Parallel.ForEach(
addresses,
addr =>
{
// create dictionary for processing
var fields = GetFields(addr);
using (var addressValidator = _addressValidatorFactory.Create())
{
// lookup
var results = addressValidator.Lookup(fields);
SetResults(addr, results);
}
});
// set entity as changed for update
addresses.ForEach(addr => entities.Entry(addr).State = EntityState.Modified);
// commit changes to db
entities.SaveChanges();
// get next set of addresses to process
addresses = ParseAddresses(allAddresses, count, batchSize).ToList();
}
}
}
查找代碼:
public ValidationResults Lookup(IDictionary<FieldEnum, string> values)
{
IDictionary<FieldEnum, string> results = null;
try
{
// load each value into accumail obj
foreach (var field in Enum.GetNames(typeof(FieldEnum)))
{
var z4Field = (Z4DLL.Field)Enum.Parse(typeof(Z4DLL.Field), field);
var fieldEnum = (FieldEnum)Enum.Parse(typeof(FieldEnum), field);
if (values.ContainsKey(fieldEnum))
{
_accumail.PutField(z4Field, values[fieldEnum] ?? string.Empty);
continue;
}
_accumail.PutField(z4Field, string.Empty);
}
// perform lookup
if (_accumail.Lookup())
{
results = new Dictionary<FieldEnum, string>();
// get each field from accumail obj
foreach (var field in Enum.GetNames(typeof(FieldEnum)))
{
results.Add((FieldEnum)Enum.Parse(typeof(FieldEnum), field),
_accumail.GetField((Z4DLL.Field)Enum.Parse(typeof(Z4DLL.Field), field)));
}
}
var errorNum = _accumail.GetErrorNum();
return new ValidationResults(results, errorNum, _accumail.GetErrorMsg(errorNum));
}
catch
{
var errorNum = _accumail.GetErrorNum();
return new ValidationResults(results, errorNum, _accumail.GetErrorMsg(errorNum));
}
}
錯誤說明:
System.AccessViolationException是聯合國處理HResult = -2147467261
消息=試圖讀取或寫入受保護的內存。這通常是指示其他內存已損壞的 。源= Z4DLL32_NET
堆棧跟蹤: 在Smartsoft.Toolkit.Z4DLL.Lookup() 在Accumail.AccumailAddressValidator.Lookup(IDictionary的2個 值)在AccumailAddressValidator.cs:線在AddressValidationService.ProcessByBatchId> b__3_0(address_validation_detail 地址)在AddressValidationService.svc.cs中:行 at System.Threading.Tasks.Parallel。 <> c__DisplayClass31_0 2.b__0(Int32 i) at System.Threading.Tasks.Parallel。 <> c__DisplayClass17_0 1.b__1() 在System.Threading.Tasks.Task.InnerInvoke() 在System.Threading.Tasks.Task.InnerInvokeWithArg(任務childTask) 在System.Threading.Tasks.Task。 <> c__DisplayClass176_0.b__0(對象 ) 在System.Threading.Tasks.Task.InnerInvoke() 在System.Threading.Tasks.Task.Execute() 在System.Threading.Tasks.Task.ExecutionContextCallback(對象OBJ) 在System.Threading.ExecutionContext.RunInternal(的ExecutionContext 的ExecutionContext,ContextCallback回調,對象的狀態,布爾 preserveSyncCtx) 在System.Threading.ExecutionContext.Run(的ExecutionContext的ExecutionContext,ContextCallback回調,對象的狀態,布爾 preserveSyncCtx) 在系統.Threading.Tasks.Task.ExecuteWithThreadLocal(任務& currentTaskSlot) at System.Threading .Tasks.Task.ExecuteEntry(布爾bPreventDoubleExecution) 在System.Threading.Tasks.Task.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem() 在System.Threading.ThreadPoolWorkQueue.Dispatch() 在System.Threading._ThreadPoolWaitCallback。PerformWaitCallback()
編輯:
類型是
private readonly Z4DLL _accumail;
這是Smartsoft.Toolkit的一部分。當被AddressValidator對象初始化,構造具有
_accumail = new Z4DLL(databasePath);
'Lookup'方法有一些最有可能導致該問題的增編代碼'_accumail.PutField(...)'。什麼是'_accumail'的類型,它是線程安全的? – sly
@sly該框架文檔顯示Accumail Z4DLL是線程安全的。 – Paul
基於'StackTrace:在Smartsoft.Toolkit.Z4DLL.Lookup()at'的例外情況,我打賭文檔不準確,或者至少不打算以這種方式使用。除了使用工廠方法'_addressValidatorFactory.Create()'以外,是否有另一種方法來實例化新的'addressValidator'? – sly