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
のStackTrace:Accumail.AccumailAddressValidator.Lookup(IDictionaryを2つの 値)AccumailAddressValidator.cs中でSmartsoft.Toolkit.Z4DLL.Lookup() で:AddressValidationService.ProcessByBatchId> b__3_0におけるライン 50(address_validation_detail ADDR )内のAddressValidationService.svc.cs:行 at System.Threading.Tasks.Parallel。 <> c__DisplayClass31_0 2.b__0(Int32 i) at System.Threading.Tasks.Parallel。 <> c__DisplayClass17_0 System.Threading.Tasks.Task.InnerInvoke()でをSystem.Threading.Tasks.Task.InnerInvokeWithArg(タスクchildTask)で に設定した場合、1.b__1() System.Threading.Tasks.Task。 < System.Threading.Tasks.Task.ExecutionContextCallback(オブジェクトOBJ)で> c__DisplayClass176_0.b__0 System.Threading.Tasks.Task.ExecuteでSystem.Threading.Tasks.Task.InnerInvokeで(オブジェクト ) () () システムでSystem.Threading.ExecutionContext.RunでSystem.Threading.ExecutionContext.RunInternal(のExecutionContext のExecutionContext、ContextCallbackコールバック、オブジェクト状態、ブール preserveSyncCtx) で(のExecutionContextのExecutionContext、ContextCallbackコールバック、状態、ブール preserveSyncCtxオブジェクト) .Threading.Tasks.Task.ExecuteWithThreadLocal(タスク& currentTaskSlot) at System.Threading System.Threading.ThreadPoolWorkQueue.DispatchでSystem.Threading.Tasks.Task.System.Threading.IThreadPoolWorkItem.ExecuteWorkItemで.Tasks.Task.ExecuteEntry(ブールbPreventDoubleExecution) () ()System.Threading._ThreadPoolWaitCallbackで 。PerformWaitCallback()
編集:
タイプSmartsoft.Toolkitの一部である
private readonly Z4DLL _accumail;
あります。 AddressValidatorが初期化されるとき、コンストラクタはあなたのDLLのインスタンスを1つだけ使用し、それを複数回呼び出して、より良い運を持っているかもしれません
_accumail = new Z4DLL(databasePath);
'Lookup'メソッドには、おそらく問題の原因となるいくつかのミュータントコード' _accumail.PutField(...) 'があります。 '_accumail'の型は何ですか?それはスレッドセーフですか? – sly
@slyフレームワークの文書では、Accumail Z4DLLはスレッドセーフです。 – Paul
例外: 'StackTrace:Smartsoft.Toolkit.Z4DLL.Lookup()at'では、ドキュメントが正確ではないか、少なくともこの方法で使用するつもりはないと賭けています。ファクトリメソッド '_addressValidatorFactory.Create()'を使う以外に新しい 'addressValidator'をインスタンス化する別の方法がありますか? – sly