我想在运行时创建类型并通过Activator.CreateInstance实例化它。我正在使用Refletion.Emit做它并且当创建和实例化类型的方法在单个线程中运行时,everithing工作正常。但是,当我尝试在多个线程中运行相同的方法时,抛出了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();
}
}
}
一些观察结果:
更新1:
异常消息是:“类型必须是运行时提供的类型。”
更新2:
完整来源托管于GitHub
答案 0 :(得分:3)
也许是迟到的答案,但无论如何。
在您的代码中,GetDynamicType
不是线程安全的,有两种竞争条件无法处理。它们中更有趣的是:
一个线程使用ModuleBuilder.DefineType(...)
定义新类型。它获取TypeBuilder
对象,然后用它来构建类型。
同时,另一个线程调用ModuleBuilder.GetType(...)
并获取它查找的Type
对象......至少它是这么认为的。它假定它得到了RuntimeType
,但实际上它得到了第一个线程构建的TypeBuilder
。
只有当第一个帖子调用TypeBuilder.CreateType()
时,TypeBuilder
才会被RuntimeType
中的相应ModuleBuilder
替换。
所以你最终尝试Activator.CreateInstance
TypeBuilder
,它会抛出“Type必须是运行时提供的类型”异常。
我建议重写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);
}