C# Automapper.CreateMap线程安全吗?

C# Automapper.CreateMap线程安全吗?,c#,multithreading,automapper,C#,Multithreading,Automapper,在我们当前的项目中,我们在由多个线程调用的类的静态构造函数中注册映射。静态构造函数中的映射仅与该类相关。但仍然可以同时运行多个CreateMap调用。此外,偶尔(主要是拷贝/过去问题)可以在不同类的静态构造函数中注册相同的映射 我试着用谷歌搜索Mapper.CreateMap是否是线程安全的。我发现只有以下几点: 在2012年的帖子中,nemesv的回答中提到CreateMap不是线程安全的,而且永远不会 但我在GitHub上发现了一个2014年的问题,该问题在3.2版本中标记为已关闭。这表明C

在我们当前的项目中,我们在由多个线程调用的类的静态构造函数中注册映射。静态构造函数中的映射仅与该类相关。但仍然可以同时运行多个CreateMap调用。此外,偶尔(主要是拷贝/过去问题)可以在不同类的静态构造函数中注册相同的映射

我试着用谷歌搜索Mapper.CreateMap是否是线程安全的。我发现只有以下几点:

在2012年的帖子中,nemesv的回答中提到CreateMap不是线程安全的,而且永远不会

但我在GitHub上发现了一个2014年的问题,该问题在3.2版本中标记为已关闭。这表明CreateMap现在应该是线程安全的

您能确认CreateMap是线程安全的吗?我做了一些测试,看起来应该是这样的,但如果有更深入的知识的人能够确认这一信息,那就好了

编辑 经过一些额外的测试,CreateMap行为似乎非常有趣:

我使用以下代码进行测试

    public void Test()
    {
        var items = new List<EntityA>();
        for (int i = 0; i < 100000; i++)
        {
            items.Add(new EntityA { FirstName = "A" + i });
        }

        ManualResetEvent stopChangingMappingFunction = new ManualResetEvent(false);

        Thread t1 = new Thread(() =>
        {

            int i = 1;
            while (true)
            {
                if (stopChangingMappingFunction.WaitOne(TimeSpan.Zero))
                    return;

                var i1 = i++;
                Mapper.CreateMap<EntityA, EntityB>().ForMember(x => x.Age, y => y.ResolveUsing(new Func<EntityA, object>(a => i1)));
            }
        });

        Thread t2 = new Thread(() =>
        {
            int i = -1;
            while (true)
            {
                if (stopChangingMappingFunction.WaitOne(TimeSpan.Zero))
                    return;

                var i1 = i--;
                Mapper.CreateMap<EntityA, EntityB>().ForMember(x => x.Age, y => y.ResolveUsing(new Func<EntityA, object>(a => i1)));
            }
        });

        List<int> distinctAges1 = null;
        List<int> distinctAges2 = null;

        Thread t3 = new Thread(() =>
        {
            Thread.Sleep(1000);

            var res = Mapper.Map<IList<EntityA>, IList<EntityB>>(items);
            distinctAges1 = res.Select(x => x.Age).Distinct().ToList();

            Thread.Sleep(1000);

            var res2 = Mapper.Map<IList<EntityA>, IList<EntityB>>(items);
            distinctAges2 = res.Select(x => x.Age).Distinct().ToList();

            stopChangingMappingFunction.Set();
        });

        t1.Start();
        t2.Start();
        t3.Start();

        t1.Join();
        t2.Join();
        t3.Join();

        Console.WriteLine("First Mapping: " + string.Join(", ", distinctAges1.ToArray()));
        Console.WriteLine("Second Mapping: " + string.Join(", ", distinctAges2.ToArray()));
        Console.ReadKey();
    }

public class EntityA
{
    public string FirstName { get; set; }
}

public class EntityB
{
    public string FirstName { get; set; }
    public int Age { get; set; }
}
公共无效测试()
{
var items=新列表();
对于(int i=0;i<100000;i++)
{
添加(新实体A{FirstName=“A”+i});
}
ManualResetEvent stopChangingMappingFunction=新的ManualResetEvent(错误);
线程t1=新线程(()=>
{
int i=1;
while(true)
{
if(stopChangingMappingFunction.WaitOne(TimeSpan.Zero))
返回;
var i1=i++;
Mapper.CreateMap().formMember(x=>x.Age,y=>y.ResolveUsing(newfunc(a=>i1));
}
});
线程t2=新线程(()=>
{
int i=-1;
while(true)
{
if(stopChangingMappingFunction.WaitOne(TimeSpan.Zero))
返回;
var i1=i--;
Mapper.CreateMap().formMember(x=>x.Age,y=>y.ResolveUsing(newfunc(a=>i1));
}
});
列表distinctAges1=null;
列表distinctAges2=null;
线程t3=新线程(()=>
{
睡眠(1000);
var res=Mapper.Map(项目);
distinctAges1=res.Select(x=>x.Age).Distinct().ToList();
睡眠(1000);
var res2=Mapper.Map(项目);
distinctAges2=res.Select(x=>x.Age).Distinct().ToList();
stopChangingMappingFunction.Set();
});
t1.Start();
t2.Start();
t3.Start();
t1.Join();
t2.连接();
t3.Join();
WriteLine(“第一个映射:”+string.Join(“,”,distinctAges1.ToArray());
WriteLine(“第二个映射:”+string.Join(“,”,distinctAges2.ToArray());
Console.ReadKey();
}
公共类实体
{
公共字符串名{get;set;}
}
公共类实体B
{
公共字符串名{get;set;}
公共整数{get;set;}
}

在我所有的测试中,当调用第一个Map方法时,这意味着CreateMap被冻结,无法对映射函数进行更多的更改(distinctAges1始终是一个唯一的值,distinctAges2中有相同的值)。从两个线程更改map函数有时会导致年龄的交替值从负数增加到正数(测试以不同年龄的高值结束)。但有时行为完全不同,年龄迭代在值为1或-1时停止。如果映射函数由多个线程更改而来,则似乎有一些内部机制冻结了对映射函数的更改。但这在100%的情况下都没有发生

CreateMap是线程安全的。这并不意味着调用CreateMap的代码是正确的。每个AppDomain只能调用一次CreateMap,通常的方法如下:


映射不应使用通过闭包传入的任何上下文数据。您上面的代码一开始就是糟糕的映射,您应该对它们进行重构,以使用上下文数据的内置方式

@usr什么使您认为它不是?至少在3.2之前它不是线程安全的。现在应该是了,但是一些“证据”就可以了,比如来自文档的证据:-)问题很清楚,我把它写给了一些人删除了评论:)请阅读这里:给Lior Dadon-请仔细阅读我的问题。我在我的问题中提到了您的链接,另外我添加了另一个链接,该链接建议CreateMap应该是3.2版的线程安全的。在这个示例中,我试图演示如何更改映射函数。在实际项目中,这不应该是一个问题。事实上,我们在多个位置(在需要映射的类的静态构造函数中)有CreateMap。原因是更好的维护(CrtMp靠近使用映射fnct的位置)。在多线程环境中,只有这种方法存在问题:一个线程调用Map,另一个线程调用CreateMap。在极少数情况下(ctrl+c、ctrl+v问题),CreateMap会尝试在多个位置注册相同的映射函数。因此,我对可能的后果感兴趣。@Jimmy链接的AutoMapperBootstrapper类已经被重构。删除该类的提交是: