【发布时间】:2017-01-17 20:43:24
【问题描述】:
尝试运行 Parallel.ForEach 从外部库 Z4DLL_NET 中查找结果。 dll 的文档说该类型是多线程安全的。我们有一个大型数据集,我们每个月都会对它进行地址验证。
当运行任何大于 1 的批处理大小时,我在 Lookup 中的 _accumail.Lookup() 上收到 Access Violation Exception 错误。
我试图通过使用 MaxDegreeOfParallelism 来减少线程数量,但它并没有阻止这个问题。任何想法将不胜感激。
网络服务代码:
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
Message=试图读取或写入受保护的内存。这通常是一个 指示其他内存已损坏。来源=Z4DLL32_NET
堆栈跟踪: 在 Smartsoft.Toolkit.Z4DLL.Lookup() 在 Accumail.AccumailAddressValidator.Lookup(字典 2 值)在 AccumailAddressValidator.cs:line 50 在 AddressValidationService.ProcessByBatchId>b__3_0(address_validation_detail addr) 在 AddressValidationService.svc.cs:line 145 在 System.Threading.Tasks.Parallel.c__DisplayClass31_0 2.b__0(Int32 一世) 在 System.Threading.Tasks.Parallel.c__DisplayClass17_0 1.b__1() 在 System.Threading.Tasks.Task.InnerInvoke() 在 System.Threading.Tasks.Task.InnerInvokeWithArg(任务子任务) 在 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 回调、对象状态、布尔值 保留SyncCtx) 在 System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback 回调, 对象状态, Boolean 保留SyncCtx) 在 System.Threading.Tasks.Task.ExecuteWithThreadLocal(任务和 currentTaskSlot) 在 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 框架文档显示 Accumail Z4DLL 是线程安全的。
-
基于异常
StackTrace: at Smartsoft.Toolkit.Z4DLL.Lookup() at我敢打赌文档不准确或至少不打算以这种方式使用。除了使用工厂方法_addressValidatorFactory.Create()之外,还有其他方法可以实例化新的addressValidator吗? -
@sly 是的,我就是这么想的。我也尝试了 var avf = new addressValidator 并且出现了同样的问题。我会在这里与公司核实。
标签: c# .net multithreading