Automapper Mapper.CreateMap是线程安全的吗?

时间:2015-08-02 08:47:46

标签: c# multithreading automapper

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

我试图谷歌是否Mapper.CreateMap是线程安全的。我发现只有以下内容:

在2012年的帖子Is Mapper.Map in AutoMapper thread-safe中,nemesv的答案中注意到CreateMap不是线程安全的,它永远不会。

但是我在2014年的GitHub Static DynamicMap and CreateMap APIs should be thread-safe上发现了一个问题,标记为在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; }
}

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

1 个答案:

答案 0 :(得分:4)

CreateMap是线程安全的。这并不意味着你的代码调用CreateMap。每个AppDomain只应调用一次CreateMap,通常的方式如下:https://github.com/jbogard/ContosoUniversity/blob/master/src/ContosoUniversity/Infrastructure/Mapping/AutoMapperBootstrapper.cs

地图不应使用通过闭包传递的任何上下文数据。您上面的代码是开头的坏地图,您应该重构它们以使用内置的上下文数据方法https://stackoverflow.com/a/31754133/58508