В нашем текущем проекте мы регистрируем сопоставление в статических конструкторах классов, которые вызывается несколькими потоками. Сопоставления в статических конструкторах применимы только для этих классов. Но все же несколько вызовов CreateMap могут быть запущены в одно и то же время. Более того, иногда (в основном как копии/прошлые выпуски) одинаковые сопоставления могут быть зарегистрированы в статических конструкторах разных классов.Является ли Automapper Mapper.CreateMap потокобезопасным?
Я попытался установить, является ли Mapper.CreateMap потокобезопасным или нет. И я нашел только следующее:
В сообщении Is Mapper.Map in AutoMapper thread-safe с 2012 года есть замечание в ответе nemesv о том, что CreateMap не является потокобезопасным, и его никогда не будет.
Но я нашел ошибку на GitHub Static DynamicMap and CreateMap APIs should be thread-safe с 2014 года, отмеченную как закрытую в 3.2 релизе. Это говорит о том, что CreateMap теперь должен быть потокобезопасным.
Можете ли вы подтвердить, что CreateMap является потокобезопасным? Я провел несколько тестов, и похоже, что это должно быть, но все же, если кто-то с более глубокими знаниями может подтвердить эту информацию, все будет хорошо.
EDIT После некоторого дополнительного тестирования, кажется, что поведение 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; }
}
Во всех моих тестах, когда метод первой карты называется это означает что CreateMap заморожен, и больше никаких изменений в функции сопоставления не может быть сделано (distinctAges1 всегда было единственным уникальным значением, а одно значение - в distinctAges2). Изменение функции отображения из двух потоков иногда приводит к увеличению чередующихся значений Возраста от отрицательного к положительному числу (тесты заканчиваются высоким значением разного возраста). Но иногда поведение было совершенно иным, и итерация возраста прекращалась при значении 1 или -1. Кажется, есть некоторые внутренние механизмы замораживания изменений в функции отображения, если эта функция сопоставления изменяется из большего количества потоков. Но этого не происходило в 100% случаях
@usr, что заставляет вас думать, что это не так? –
Он не был нитевым, по крайней мере, до 3.2. Теперь это должно быть, но некоторые «доказательства», например. от документации было бы хорошо :-) – user2126375
Вопрос ясен, я написал его кому-то удаленному комментарию :) –