Parallel.ForEach

时间:2016-09-09 16:27:53

标签: c# .net multithreading

尝试运行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);

1 个答案:

答案 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循环中拉出来。