尝试运行Parallel.ForEach以从外部库Z4DLL_NET查找结果。 dll的文档说这种类型是多线程安全的。我们有一个大型数据集,我们每个月都在进行地址验证。
当运行任何批量大于1的批处理时,我在查找中的_accumail.Lookup()上收到了访问冲突异常错误。
我试图通过使用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(IDictionary 2 AccumailAddressValidator.cs:line中的值) 50 在AddressValidationService.ProcessByBatchId&gt; b__3_0(address_validation_detail) addr)在AddressValidationService.svc.cs:行中 145 在System.Threading.Tasks.Parallel。&lt;&gt; c__DisplayClass31_0 2.b__0(Int32 一世) 在System.Threading.Tasks.Parallel。&lt;&gt; c__DisplayClass17_0 1.b__1() 在System.Threading.Tasks.Task.InnerInvoke() 在System.Threading.Tasks.Task.InnerInvokeWithArg(任务childTask) 在System.Threading.Tasks.Task。&lt;&gt; c__DisplayClass176_0.b__0(对象) ) 在System.Threading.Tasks.Task.InnerInvoke() 在System.Threading.Tasks.Task.Execute() 在System.Threading.Tasks.Task.ExecutionContextCallback(Object obj) 在System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext,ContextCallback回调,对象状态,布尔值 preserveSyncCtx) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext,ContextCallback callback,Object state,Boolean preserveSyncCtx) 在System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task&amp; currentTaskSlot) 在System.Threading.Tasks.Task.ExecuteEntry(Boolean 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);
答案 0 :(得分:0)
如果只使用一个dll实例并多次调用它,你可能会有更好的运气。
using (var addressValidator = _addressValidatorFactory.Create())
{ {
Parallel.ForEach(
addresses,
addr =>
{
// create dictionary for processing
var fields = GetFields(addr);
// lookup
var results = addressValidator.Lookup(fields);
SetResults(addr, results);
}
});
}
注意以上是作为一个例子 - 我没有测试你的逻辑,看看这是否可行而不做其他改动。关键是要说明如何将它从for循环中拉出来。