使用 Activator C# 测试线程安全

Testing Thread Safety With Activator C#

我正在家里做一个小项目,试图提高我的编码技能。我目前正在建设一家工厂。我告诉一些同事它的实现方式,他们告诉我它可能存在线程和死锁问题。我创建了一个单元测试,但我不确定这是否有效或者是否会发现问题。这是我的设置。

public class InventoryFactory : BaseFactory, IInventoryFactory
{
    public IInventoryEngine Create(InventoryFactoryConfiguration configuration)
    {
        IInventoryRepository repository = BuildInventoryRepository(configuration.RepositoryConfiguration);
        IInventoryService service = BuildInventoryService(configuration.ServiceConfiguration);
        IInventoryEngine engine = BuildInventoryEngine(configuration.EngineConfiguration, repository, service);
        return engine;
    }

    private IInventoryRepository BuildInventoryRepository(FactoryConfigurationModel configuration)
    {
        IInventoryRepository repository =
            base.CreateInstance<IInventoryRepository>(configuration.AssemblyFile, configuration.FullyQualifiedClassName);

        return repository;
    }

    private IInventoryService BuildInventoryService(FactoryConfigurationModel configuration)
    {
        IInventoryService service =
            base.CreateInstance<IInventoryService>(configuration.AssemblyFile, configuration.FullyQualifiedClassName);

        return service;
    }

    private IInventoryEngine BuildInventoryEngine(FactoryConfigurationModel configuration, IInventoryRepository repository, IInventoryService service)
    {
        IInventoryEngine engine =
            base.CreateInstance<IInventoryEngine>(configuration.AssemblyFile, configuration.FullyQualifiedClassName, repository, service);

        return engine;
    }
}

public class BaseFactory
{
    protected T CreateInstance<T>(string assemblyFile, string fullyQualifiedClassName, params object[] arguments)
    {
        Assembly assembly = Assembly.LoadFrom(assemblyFile);
        Type classType = assembly.GetType(fullyQualifiedClassName);
        T instance = (T)Activator.CreateInstance(classType, arguments);
        return instance;
    }
}

这是我当前的单元测试,尝试查看是否会出现任何问题。

[TestMethod]
    public void InventoryFactoryThreadTest()
    {
        Task task1 = Task.Factory.StartNew(() =>
        {
            FactoryConfigurationModel repositoryConfiguration = new FactoryConfigurationModel("foo", "bar");
            FactoryConfigurationModel serviceConfiguration = new FactoryConfigurationModel("foo", "bar");
            FactoryConfigurationModel engineConfiguration = new FactoryConfigurationModel("foo", "bar");
            InventoryFactoryConfiguration factoryConfiguration = new InventoryFactoryConfiguration(repositoryConfiguration, serviceConfiguration, engineConfiguration);
            InventoryFactory factory = new InventoryFactory();

            try
            {
                for (int i = 0; i < 150000; i++)
                {
                    IInventoryEngine engine = factory.Create(factoryConfiguration);
                }
            }
            catch (System.Exception ex)
            {
                Assert.Fail(ex.StackTrace);
            }
        });

        Task task2 = Task.Factory.StartNew(() =>
        {
            FactoryConfigurationModel repositoryConfiguration = new FactoryConfigurationModel("foo", "bar");
            FactoryConfigurationModel serviceConfiguration = new FactoryConfigurationModel("foo", "bar");
            FactoryConfigurationModel engineConfiguration = new FactoryConfigurationModel("foo", "bar");
            InventoryFactoryConfiguration factoryConfiguration = new InventoryFactoryConfiguration(repositoryConfiguration, serviceConfiguration, engineConfiguration);
            InventoryFactory factory = new InventoryFactory();

            try
            {
                for (int i = 0; i < 150000; i++)
                {
                    IInventoryEngine engine = factory.Create(factoryConfiguration);
                }
            }
            catch (System.Exception ex)
            {
                Assert.Fail(ex.StackTrace);
            }
        });

        Task task3 = Task.Factory.StartNew(() =>
        {
            FactoryConfigurationModel repositoryConfiguration = new FactoryConfigurationModel("foo", "bar");
            FactoryConfigurationModel serviceConfiguration = new FactoryConfigurationModel("foo", "bar");
            FactoryConfigurationModel engineConfiguration = new FactoryConfigurationModel("foo", "bar");
            InventoryFactoryConfiguration factoryConfiguration = new InventoryFactoryConfiguration(repositoryConfiguration, serviceConfiguration, engineConfiguration);
            InventoryFactory factory = new InventoryFactory();

            try
            {
                for (int i = 0; i < 150000; i++)
                {
                    IInventoryEngine engine = factory.Create(factoryConfiguration);
                }
            }
            catch (System.Exception ex)
            {
                Assert.Fail(ex.StackTrace);
            }
        });

        Task.WaitAll(task1, task2, task3);
    }

据我了解,潜在的线程问题与 Activator.CreateInstance() 方法有关。我的问题是:这是一个能够查看线程是否不安全的有效测试。如果不是,你能告诉我为什么吗?

提前谢谢!

我没有看到线程问题,因为您正在创建和返回类型 T 的新的单个实例。可能想看看 Assembly.LoadFrom 是否线程安全。

安全起见:

public class BaseFactory
{
    private static object _monitor = new object();
    protected T CreateInstance<T>(string assemblyFile, string fullyQualifiedClassName, params object[] arguments)
    {
        lock(_monitor)
        {
            Assembly assembly = Assembly.LoadFrom(assemblyFile);
            Type classType = assembly.GetType(fullyQualifiedClassName);
            T instance = (T)Activator.CreateInstance(classType, arguments);
            return instance;
        }
    }
}

调用 Activator.CreateInstance 与调用 new Something() 实际上是一样的。除非 class 的构造函数中有某些东西在做一些影响某些共享状态的非常奇怪的事情,否则线程安全和死锁不会成为问题。

发明轮子很有趣,但我建议您看看已经发明的一些。 Windsor、Autofac 和其他 Ioc/DI 容器非常擅长创建对象实例,包括为接口实现扫描指定程序集。您可以指定是否希望它每次都创建一个新的 class 实例,或者一遍又一遍地 return 相同的实例。您不必调用构造函数,这使得创建具有大量复杂嵌套依赖项的 classes 变得更加容易。这反过来又让您可以自由地编写更小、更可测试的 classes,而不必担心如何构建它们。这很棒。如果您正处于需要创建这样的工厂的地步,那么可能是时候看看 IoC 容器了。

此外,测试是否线程安全时存在问题。多线程问题是不可预测的,因此很难通过测试排除它们。如果某些东西不是线程安全的,它可以通过各种测试,但在另一个环境中会爆炸。这并不是说测试不好。只是永远不会 100% 定论。