测试线程安全性的单元测试-对象不是随机可用的

本文关键字:随机 线程 安全性 单元测试 测试 对象 | 更新日期: 2023-09-27 18:17:57

我们有一些遗留代码在许多类上测试线程安全性。最近的一次硬件升级(从2核到4核)出现了随机故障,从List<>访问一个项目时出现了异常。

        [Test]
        public void CheckThreadSafeInThreadPool()
        {
            Console.WriteLine("Initialised ThreadLocalDataContextStore...");
            var container = new ContextContainerTest();
            Console.WriteLine("Starting...");
            container.StartPool();
            while (container.ThreadNumber < 5)
            {
                Thread.Sleep(1000);
            }
            foreach (var message in container.Messages)
            {
                Console.WriteLine(message);
                if (message.Contains("A supposedly new thread is able to see the old value"))
                {
                    Assert.Fail("Thread leaked values - not thread safe");
                }
            }
            Console.WriteLine("Complete");
        }


public class ContextContainerTest
    {
        private ThreadLocalDataContextStore store;
        public int ThreadNumber;
        public List<string> Messages;
        public void StartPool()
        {
            Messages = new List<string>();
            store = new ThreadLocalDataContextStore();
            store.ClearContext();
            var msoContext = new MsoContext();
            msoContext.Principal = new GenericPrincipal(new GenericIdentity("0"), null);
            store.StoreContext(msoContext);
            for (var counter = 0; counter < 5; counter++)
            {
                Messages.Add(string.Format("Assigning work item {0}", counter));
                ThreadPool.QueueUserWorkItem(ExecuteMe, counter);
            }
        }
        public void ExecuteMe(object input)
        {
            string hashCode = Thread.CurrentThread.GetHashCode().ToString();
            if (store.GetContext() == null || store.GetContext().Principal == null)
            {
                Messages.Add(string.Format("[{0}] A New Thread", hashCode));
                var msoContext = new MsoContext();
                msoContext.Principal = new GenericPrincipal(new GenericIdentity("2"), null);
                store.StoreContext(msoContext);
            }
            else if (store.GetContext().Principal.Identity.Name == "1")
            {
                Messages.Add(string.Format("[{0}] Thread reused", hashCode));
            }
            else
            {
                Messages.Add(string.Format("[{0}] A supposedly new thread is able to see the old value {1}"
                    , hashCode, store.GetContext().GetDiagnosticInformation()));
            }
            Messages.Add(string.Format("[{0}] Context at starting: {1}", hashCode, store.GetContext().GetDiagnosticInformation()));
            store.GetContext().SetAsCurrent(new GenericPrincipal(new GenericIdentity("99"), null));
            Messages.Add(string.Format("[{0}] Context at End: {1}", hashCode, store.GetContext().GetDiagnosticInformation()));
            store.GetContext().SetAsCurrent(new GenericPrincipal(new GenericIdentity("1"), null));
            Thread.Sleep(80);
            ThreadNumber++;
        }

    }

故障是随机的,并且发生在测试本身的代码的以下部分;

        foreach (var message in container.Messages)
        {
            Console.WriteLine(message);
            if (message.Contains("A supposedly new thread is able to see the old value"))
            {
                Assert.Fail("Thread leaked values - not thread safe");
            }
        }

一个微妙的改变解决了这个问题,但是有人抱怨我们不应该这样做,为什么消息是空的,如果消息不是,为什么它在大多数时候工作,而不是其他。

 if (message != null && message.Contains("A supposedly new thread is able to see the old value"))
{
}

另一个解决方案是将List更改为线程安全的,但这并不能回答为什么问题首先出现。

测试线程安全性的单元测试-对象不是随机可用的

List<T>不是线程安全元素,如果你使用。net 4及以上,你可以使用System.Collection.Concurrent中的ConcurrentBag<T>,如果旧的你必须自己实现一个。这可能会有帮助。

希望对您有所帮助。