[英]why is the lock keyword not working with this multi-threading implementation?
我正在尝试将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;
});
}
}
}
问题是临时adUser
变量是在Parallel.ForEach
块的外部范围中定义的,这导致不同的线程对其进行修改。 相反,请考虑将定义移到foreach主体中,在该主体中实际设置变量。 这样,每个线程将获得其自己的变量。 当然,这里的问题是Parallel.Foreach是否对重复的引用进行操作,但我认为没有。
在这里,我将如何修改您的代码: https : //dotnetfiddle.net/gFb5z6
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.