0
我们有一些遗留代码可以测试多个类的线程安全性。最近的硬件升级(从2核心到4核心)呈现随机故障,但访问列表<>中的项目时出现异常。单元测试测试线程安全性 - 对象不随机提供
[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"))
{
}
另一种解决方案是将列表更改为线程安全,但这并不能解答为什么问题首先出现。
它看起来不像一个单元测试 - 它并不简单,不清楚,似乎只测试一件事情并且具有复杂的设置逻辑。将列表转换为线程安全类,或者在访问列表时使用锁 - 这可能是某种竞争条件。您已经花费了更多时间来输入此问题,而不仅仅是更改代码以在多线程环境中使用正确的数据结构。另外,只要删除这个测试 - 它闻起来很糟糕。 – oleksii
“另一个解决方案是将列表更改为线程安全,但这并不能解释为什么问题首先出现” - 但问题是因为List不是线程安全的,您需要什么其他原因? – Evk