2015-05-12 2 views
4

Я хочу создавать типы во время выполнения и создавать его через Activator.CreateInstance. Я использую Refletion.Emit, чтобы сделать это, и everithing прекрасно работает, когда метод, который создает и создает экземпляр типа работает в одном потоке. Ховер, когда я пытаюсь запустить тот же метод в более чем одном потоке, возникает ArgumentException.Activator.CreateInstance типа, возвращаемого с TypeBuilder.CreateType throws ArgumentException

код аналогичен:

class TypeBuilder 
    public IMyType Build() { 
    Type type = GetDynamicType("MyDynamicType"); 
    IMyType myType = (IMyType) Activator.CreateInstance(type); 
    return myType; 
    } 

    Type GetDynamicType(string typeName) { 
    // define the module builder... 
    ModuleBuilder module = ... 
    Type type = module.GetType(typeName); 
    if (type == null) { 
     type = MakeDynamicType(typeName); 
    } 
    retyrn type; 
    } 

    Type MakeDynamicType(string typeName) { 
    lock(lock_) { // lock_ is a static variable 
     // ensure that the type was not already created by another thread. 
     Type type = 
      module 
      .GetType(typeName); 
     if (type != null) { 
      return type; 
     } 
     // define the type builder... 
     TypeBuilder builder = ... 

     // define the type body... 

     return type.CreateType(); 
    } 
    } 
} 

Некоторые наблюдения:

  • Исключение выбрасывается только тогда, когда более, что один поток попытается создать тип.
  • При первом вызове метода исключение выбрасывает возвращаемый тип из TypeBuilder, а не из RunTimeType, но во второй раз, когда метод называется типом, вызывается из RunTimeType.

Обновление 1:

Сообщение исключение: «Тип должен быть типом предусмотрено время выполнения»

Update 2:

Полный источник размещается на GitHub

+0

Что фактическое сообщение исключение? Есть ли внутренние исключения? Пожалуйста, вставьте их. Кроме того, поскольку это многопоточность, можете ли вы гарантировать, что вы создали тип сначала, прежде чем другой поток попытался создать экземпляр? – TyCobb

+0

Да, я проверяю тип для существования, прежде чем создавать его. – nohros

+3

Пожалуйста, разместите свой полный код, а не «аналогичный» выше, можете ли вы также включить и пример того, как вы могли бы также называть «TypeBuilder». Благодарю. –

ответ

3

Может быть поздно ответ, но в любом случае.

В вашем коде GetDynamicType не является потокобезопасным, существуют два условия гонки, которые не обрабатываются. Более интересным из них является следующее:

Один поток определяет новый тип с ModuleBuilder.DefineType(...). Он получает TypeBuilder объект, который затем использует для построения типа.

В то же время другая нить вызывает ModuleBuilder.GetType(...) и получает объект Type, который он ищет ... по крайней мере, он так думает. Предполагается, что он получил RuntimeType, но на самом деле он получает TypeBuilder, созданный по первому потоку.

Только тогда, когда первый поток вызывает TypeBuilder.CreateType(), то TypeBuilder заменяется соответствующим RuntimeType в ModuleBuilder.

И поэтому вы в конечном итоге пытаетесь установить Activator.CreateInstance из TypeBuilder, который генерирует исключение типа «Тип должен быть предоставлен за счет выполнения».

Я хотел бы предложить переписывания GetDynamicType функцию и использовать ConcurrentDictionary<string, Type> с MakeDynamicType в качестве значения завода:

private readonly ConcurrentDictionary<string, Type> _dynamicTypesByName = 
     new ConcurrentDictionary<string, Type>(); 

    Type GetDynamicType(string typeName) { 
    // define the module builder... 
    ModuleBuilder module = ... 
    return _dynamicTypesByName.GetOrAdd(typeName, MakeDynamicType); 
    } 
+0

Спасибо за помощь, проблема была в состоянии гонки, и я решил ее с помощью lock() внутри GetDynamicType до GetType. – nohros

Смежные вопросы