我正在创建一个对输入执行顺序处理的组件。由于它将托管在几个不同的进程中,我需要它是线程安全的。起初,我故意从代码中省略了线程安全性。现在是时候介绍一下了。
首先,我想引发一个错误,但是无法做到。以下是处理引擎代码的简化版本:
public Document DoOrchestration(Document input)
{
Document output = new Document();
foreach (var orchestrationStep in m_OrchestrationSteps)
{
var processor = GetProcessor(orchestrationStep).Clone();
output = processor.Process(input);
input = output;
}
return output;
}
处理器可以由我组织中的其他人开发,这可能包括一些复杂的初始化。它们也可能是线程不安全的,所以我使用Prototype Pattern来获取它的唯一实例,以避免线程问题。
为了测试这个功能,我使用了以下代码:
for (int i = 0; i < 20000; i++)
{
Thread t = new Thread(() => TestOrchestration(i));
t.Start();
}
void TestOrchestration(int number)
{
Document doc = new Document(string.Format("Test {0}", number));
doc = DoOrchestration(doc);
if (doc.ToString().Substring(0,35) != strExpectedResult)
{
System.Console.WriteLine("Error: {0}", doc.ToString();
}
}
我预计有些线程会与另一个线程发生冲突并混淆其结果,但令我惊讶的是,这并没有发生。
对此可能有一个简单而合理的解释,但它让我望而却步。或者只是代码太简单而导致两个线程同时摆弄输入/输出变量?
答案 0 :(得分:1)
查看CHESS。
CHESS是一种同时发现和复制Heisenbugs的工具 程式。 CHESS反复运行并发测试,确保每一个 run需要不同的交错。如果交错产生一个 错误,CHESS可以重现交错以改进调试。 CHESS适用于托管和本机程序。
答案 1 :(得分:0)
我认为由于测试功能的简单性,你的线程甚至没有时间在前一个完成它的工作之前产生大量的时间。在开始计算步骤之前,请考虑使用barrier允许所有线程生成。此外,您还需要考虑增加测试用例的复杂性,例如通过在同一个循环中执行多个相同的操作(启动一个线程很昂贵,并允许其他内核完成他们的工作,然后再进入资源争夺。
通常,可以通过在较长时间内快速访问相同资源来激发资源争用,您的测试用例似乎不允许这样做。顺便说一句,我强烈建议您考虑线程安全,而不是稍后介绍。使用代码时,您可以更好地理解资源访问模式,而不是在稍后分析代码时。
答案 2 :(得分:0)
我认为你的测试函数在下一个线程启动之前几乎完成了。您可以在调用业务流程函数之前使所有线程在ManualResetEventSlim上等待,然后设置ManualResetEventSlim
这样,所有线程都会尝试同时调用业务流程。
如果所有线程男性管弦乐队几乎同时调用,你也可能不需要20,000个线程来模拟这种行为
ManualResetEventSlim manualEvent = new ManualResetEventSlim (false);
for (int i = 0; i < 20000; i++)
{
Thread t = new Thread(() => TestOrchestration(i));
t.Start();
}
manualEvent.Set();
void TestOrchestration(int number)
{
manualEvent.Wait();
Document doc = new Document(string.Format("Test {0}", number));
doc = DoOrchestration(doc);
if (doc.ToString().Substring(0,35) != strExpectedResult)
{
System.Console.WriteLine("Error: {0}", doc.ToString();
}
}
答案 3 :(得分:0)
您可以使用ManualResetEvent
同时继续任意数量的等待线程。