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");
}
}
微妙な変更が問題を解決し、誰かが、我々はメッセージがないと、なぜそれが他のほとんどの時間を仕事としていない場合、なぜメッセージがnullである、ことを実行する必要がないことをやっかいされます。
if (message != null && message.Contains("A supposedly new thread is able to see the old value"))
{
}
別の解決策は、スレッドセーフであることをリストを変更することでしたが、問題は最初の場所で発生した理由をお答えdoesntの。
単体テストのようには見えません。単純ではなく、はっきりしていないし、1つのことしかテストしていないようで、複雑なセットアップロジックがあります。あなたのリストをスレッドセーフなクラスに変換するか、リストにアクセスするときにロックを使用してください。何らかの競合状態になる可能性があります。あなたは、マルチスレッド環境で正しいデータ構造を使用するためにコードを変更するだけではなく、この質問を入力するより多くの時間を費やしました。または、このテストを削除するだけで、悪い匂いがします。 – oleksii
"もう1つの解決策は、リストをスレッドセーフに変更することでしたが、なぜ問題が最初に発生したのか答えませんでした" - しかし、問題はリストがスレッドセーフであるためです。 – Evk