2016-09-09 70 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
堆棧跟蹤: 在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); 
+0

'Lookup'方法有一些最有可能導致該問題的增編代碼'_accumail.PutField(...)'。什麼是'_accumail'的類型,它是線程安全的? – sly

+0

@sly該框架文檔顯示Accumail Z4DLL是線程安全的。 – Paul

+0

基於'StackTrace:在Smartsoft.Toolkit.Z4DLL.Lookup()at'的例外情況,我打賭文檔不準確,或者至少不打算以這種方式使用。除了使用工廠方法'_addressValidatorFactory.Create()'以外,是否有另一種方法來實例化新的'addressValidator'? – sly

回答

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循環。

+0

我得到了與上面相同的錯誤。所以我只使用一次工廠。 – Paul