【问题标题】:What's wrong with this asynchronous code?这个异步代码有什么问题?
【发布时间】:2018-05-22 20:03:35
【问题描述】:

我有下面的代码,它从 Active Directory 中检索组。当我只为MY_GROUP_NAME 运行代码时(在下面注释掉),输出与预期一致。

当我从 AD 运行整个组时,最终的数据集是不正确的。一个具体的例子是,我最终在列表中有多个 adGroups 具有相同的组名称但不同的 ParentGroupGuid。这是一个无效的场景。这个问题似乎与Parallel.ForEach() 调用下面的递归方法有关。

知道问题可能是什么以及如何解决吗?

private ConcurrentBag<Core.Models.ADGroup> adGroups;        

public async Task<List<Core.Models.ADGroup>> GetADGroupsFromADAsync(string domainName)
{
    return await Task.Run(async() =>
    {
        var domainId = await new DomainRepository().GetDomainId(domainName);

        using (var context = new PrincipalContext(ContextType.Domain, domainName))
        {
            var ps = new PrincipalSearcher(new GroupPrincipal(context));
            Parallel.ForEach(
                ps.FindAll().ToList(),
                //ps.FindAll().Where(x => x.Name == "MY_GROUP_NAME").ToList(),
                new ParallelOptions() { MaxDegreeOfParallelism = Environment.ProcessorCount },
                async (group, loopState) =>
                {
                    await GetGroupsRecursive((Guid)domainId, null, (GroupPrincipal)group);
                });
        }

        //return group list
        return adGroups.ToList();
    });
}

private async Task GetGroupsRecursive(Guid domainId, Guid? parentGroupGuid, GroupPrincipal group)
{
    //cast result to adgroup
    var adGroup = Mapper.Map<Core.Models.ADGroup>(group);

    //set domainid
    adGroup.DomainId = domainId;

    //set parent group id
    adGroup.ParentGroupGuid = parentGroupGuid;

    //process child groups
    foreach (var member in group.Members)
        if (member is GroupPrincipal)
            await GetGroupsRecursive(domainId, adGroup.Guid, (GroupPrincipal)member);

    //add to the list
    adGroups.Add(adGroup);
}

【问题讨论】:

  • 请看我更新的答案,还有第二个更重要的问题,它更可能是您的错误的根源。

标签: c# .net parallel-processing active-directory


【解决方案1】:

同时使用来自多个线程的PrincipalContext 是不安全的。在内部,您在 GetGroupsRecursive will call ContextRaw.QueryCtx.GetGroupMembership(this, false); 中使用的 group.Members 调用使用 Principal Context。

这可能会导致您遇到的错误。您要么需要每个线程的上下文,要么不需要多线程成员查找。

编辑:你的代码还有另一个主要问题(直到我试图写一个例子才看到它),你正在使用 async/await 和 Parallel.ForEach 调用。 不支持,你只能用Parallel.ForEach做同步方法,去掉异步或者切换到TPL Dataflow。

这是一个修复 async/await 并使其成为每个线程的上下文的示例

public async Task<List<Core.Models.ADGroup>> GetADGroupsFromADAsync(string domainName)
{
    return await Task.Run(async() =>
    {
        var domainId = await new DomainRepository().GetDomainId(domainName);

        using (var searchContext = new PrincipalContext(ContextType.Domain, domainName))
        {
            var ps = new PrincipalSearcher(new GroupPrincipal(searchContext));
            Parallel.ForEach(
                ps.FindAll().Select(x=>x.DistinguishedName),
                new ParallelOptions() { MaxDegreeOfParallelism = Environment.ProcessorCount },
                () => new PrincipalContext(ContextType.Domain, domainName),
                (distinguishedName, loopState, threadLocalContext) =>
                {
                    var threadLocalGroup = GroupPrincipal.FindByIdentity(threadLocalContext, IdentityType.DistinguishedName, distinguishedName);
                    GetGroupsRecursive((Guid)domainId, null, threadLocalGroup);
                    return threadLocalContext;
                },
                threadLocalContext => threadLocalContext.Dispose());
        }

        //return group list
        return adGroups.ToList();
    });
}

private void GetGroupsRecursive(Guid domainId, Guid? parentGroupGuid, GroupPrincipal group)
{
    //cast result to adgroup
    var adGroup = Mapper.Map<Core.Models.ADGroup>(group);

    //set domainid
    adGroup.DomainId = domainId;

    //set parent group id
    adGroup.ParentGroupGuid = parentGroupGuid;

    //process child groups
    foreach (var member in group.Members)
        if (member is GroupPrincipal)
            GetGroupsRecursive(domainId, adGroup.Guid, (GroupPrincipal)member);

    //add to the list
    adGroups.Add(adGroup);
}

【讨论】:

    猜你喜欢
    • 2017-11-16
    • 1970-01-01
    • 2012-08-07
    • 2014-08-29
    • 2013-06-05
    • 2014-06-04
    • 2011-07-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多