为什么lock关键字无法使用此多线程实现?

时间:2018-04-16 04:26:30

标签: c# .net multithreading

我正在尝试使用Parallel.ForEach()构造进行多线程,我遇到了一些问题。具体来说,一些数据需要以不同的方式进行评估,这似乎在ForEach()构造中产生了一些问题,其中一些属性被设置为来自不同线程的值。我以为我可以简单地用锁来包装那个特定的代码,但问题仍然存在。您可以通过查看以下代码来确定问题所在吗?:

https://dotnetfiddle.net/IDUGbP

这是内联代码:

using SecurityApi.Core.Models;
using SecurityApi.Core.Repositories;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.DirectoryServices.AccountManagement;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using AutoMapper;
using SecurityApi.EntityFramework;
using System.DirectoryServices;

namespace SecurityApi.Repositories
{
    [Export(typeof(IADUserRepository))]
    public class ADUserRepository : IADUserRepository
    {
        private Object etlLock = new Object();

        public async Task<List<Core.Models.ADUser>> GetADUsersFromAD(string domainName)
        {
            var domainId = await new DomainRepository().GetDomainId(domainName);
            var adUsers = new ConcurrentBag<Core.Models.ADUser>();
            var adGroups = new ConcurrentBag<Core.Models.ADGroup>();

            return await Task.Run(() =>
            {
                using (var context = new PrincipalContext(ContextType.Domain, domainName))
                {
                    // init vars
                    var up = new UserPrincipal(context);
                    var ps = new PrincipalSearcher(up);
                    Core.Models.ADUser adUser;

                    Parallel.ForEach(
                        ps.FindAll().ToList(), 
                        new ParallelOptions() { MaxDegreeOfParallelism = Environment.ProcessorCount }, 
                        result =>
                    {
                        if (!result.DistinguishedName.Contains("OU=Users")) return;

                        //set ad user properties
                        var userPrincipal = result as UserPrincipal;
                        adUser.Name = userPrincipal.Name;
                        adUser.FirstName = userPrincipal.GivenName;
                        adUser.LastName = userPrincipal.Surname;

                        lock (etlLock)
                        {
                            //get a directory entry representation for accessing special properties
                            var directoryEntry = result.GetUnderlyingObject() as DirectoryEntry;

                            //user principal name
                            adUser.UserPrincipalName = directoryEntry.Properties["UserPrincipalName"].Value?.ToString();

                            //employee id
                            adUser.EmployeeId = directoryEntry.Properties["EmployeeId"].Value?.ToString();
                        }

                        #endregion

                        adUsers.Add(adUser);
                    });
                }

                return adUsersList;
            });
        }
    }
}

1 个答案:

答案 0 :(得分:1)

问题是临时adUser变量是在Parallel.ForEach块的外部作用域中定义的,这导致不同的线程修改它。相反,考虑将该定义移动到foreach的主体,在那里实际设置变量。这样每个线程都会获得它自己的变量。当然,这里的问题是是否存在Parallel.Foreach操作的重复引用,但我认为没有&#t; t。

以下是我修改代码的方式:https://dotnetfiddle.net/gFb5z6