2016-09-09 5 views
0

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); 
+0

'Lookup'メソッドには、おそらく問題の原因となるいくつかのミュータントコード' _accumail.PutField(...) 'があります。 '_accumail'の型は何ですか?それはスレッドセーフですか? – sly

+0

@slyフレームワークの文書では、Accumail Z4DLLはスレッドセーフです。 – Paul

+0

例外: 'StackTrace:Smartsoft.Toolkit.Z4DLL.Lookup()at'では、ドキュメントが正確ではないか、少なくともこの方法で使用するつもりはないと賭けています。ファクトリメソッド '_addressValidatorFactory.Create()'を使う以外に新しい 'addressValidator'をインスタンス化する別の方法がありますか? – sly

答えて

0

を持っています。

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); 
       } 
      }); 
} 

NOTE上記の例として意図されている - 私は、これは他の変更を加えることなく動作するかどうかを確認するためにあなたのロジックをテストしませんでした。要点は、forループからどのように抜けることができるかを示すことです。

+0

私は上記と同じエラーが発生します。私は一度だけ工場を使っています。 – Paul

関連する問題