Когда динамический модуль может иметь исключение загрузки типа?

У меня есть динамический модуль, который добавляет к нему типы по мере запуска моего приложения. Модуль создается с помощью следующего кода:

var assemblyName = new AssemblyName("MyAssembly");
var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
MyClass.RuntimeBoundDerivedTypesModule = assemblyBuilder.DefineDynamicModule("MainModule");

Другие части приложения также иногда вызывают GetTypes() для сборки модуля. Иногда, когда это происходит, я получаю TypeLoadException для одного из типов в динамическом модуле. Трассировка стека:

   at System.Reflection.RuntimeModule.GetTypes(RuntimeModule module)
   at System.Reflection.RuntimeModule.GetTypes()
   at System.Reflection.Assembly.GetTypes()

Мой вопрос: что может вызвать это исключение? Являются ли модули среды выполнения действительно потокобезопасными или могут быть условия гонки, когда GetTypes() вызывается, когда тип находится на полпути к созданию?

РЕДАКТИРОВАТЬ: вот небольшой фрагмент кода, который надежно воспроизводит ошибку для меня. Теперь кажется, что исключение возникает, если GetTypes() вызывается между DefineType() и CreateType():

var assemblyName = new AssemblyName("MyAssembly");
var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
ModuleBuilder m = assemblyBuilder.DefineDynamicModule("foo");

Action doStuff = () => {
    try {
        if (!m.GetTypes().Any() || Guid.NewGuid().GetHashCode() % 2 == 0) {
            var t = m.DefineType(
                "MyType" + Guid.NewGuid().ToString().Replace("-", ""),
                TypeAttributes.Public | TypeAttributes.Class | TypeAttributes.AutoClass | TypeAttributes.AnsiClass | TypeAttributes.BeforeFieldInit | TypeAttributes.AutoLayout,
                typeof(object)
            );
            Thread.Sleep(1);
            t.CreateType();
        }
        else {
            //m.GetTypes(); // interestingly, this always works
            assemblyBuilder.GetTypes();
            "it worked!".Dump();
        }

    } catch (Exception ex) {
        Console.WriteLine(ex);
    }
};

// in synchronous mode, I only see failures if I leave out the call to CreateType(). In that
// case, it never works
//Enumerable.Range(1, 1000)
//  .ToList()
//  .ForEach(_ => doStuff());

// in the async mode, I always see failures with Thread.Sleep() and sometimes
// see them if I take the sleep out. I often see a mix of failures and "It worked!"
var threads = Enumerable.Range(1, 100).Select(t => new Thread(() => doStuff()))
    .ToList();
threads.ForEach(t => t.Start());
threads.ForEach(t => t.Join());

person ChaseMedallion    schedule 22.08.2013    source источник


Ответы (1)


ModuleBuilder и фактически большинство методов экземпляра не являются потокобезопасными в .Net. См. здесь внизу: http://msdn.microsoft.com/en-us/library/system.reflection.emit.modulebuilder.aspx

person JaCraig    schedule 28.08.2013