Jon*_*Jon 10 .net c# multithreading unit-testing
我正在编写一个多线程应用程序,并试图找出如何为它编写单元测试.我认为这可能是另一个问题,关于如何最好地做到这一点.无论如何,我有一个像下面这样的类知道它不是线程安全的,并且想要在单元测试中证明它但是无法解决如何做到这一点:
public class MyClass
{
private List<string> MyList = new List<string>();
public void Add(string Data)
{
MyList.Add(Data); //This is not thread safe!!
}
}
Run Code Online (Sandbox Code Playgroud)
Mar*_*ell 13
证明某些东西是线程安全的是棘手的 - 可能很难解决问题.您可以证明竞争条件很容易产生,或者很难生产.但不产生竞争条件并不意味着它不存在.
但是:我在这里常用的方法(如果我有理由认为一些代码应该是线程安全的,不是)就是在一个ManualResetEvent后面等待很多线程.到达门的最后一个线程(使用interlocked to count)负责打开门,以便所有线程同时(并且已经存在)命中系统.然后他们完成工作并检查理智的退出条件.然后我重复这个过程很多次.这通常足以重现一个可疑的线程竞赛,并表明它从"明显破碎"变为"没有以明显方式破坏"(这与"未破坏"至关重要).
另请注意:大多数代码不必是线程安全的.
我有一个类似的问题,我们发现了线程安全错误。为了修复它,我们必须证明它,然后修复它。那个任务把我带到了这个页面,但我找不到任何真正的答案。由于上述许多答案解释了原因。但无论如何,我找到了一种可能帮助他人的方法:
public static async Task<(bool IsSuccess, Exception Error)> RunTaskInParallel(Func<Task> task, int numberOfParallelExecutions = 2)
{
var cancellationTokenSource = new CancellationTokenSource();
Exception error = null;
int tasksCompletedCount = 0;
var result = Parallel.For(0, numberOfParallelExecutions, GetParallelLoopOptions(cancellationTokenSource),
async index =>
{
try
{
await task();
}
catch (Exception ex)
{
error = ex;
cancellationTokenSource.Cancel();
}
finally
{
tasksCompletedCount++;
}
});
int spinWaitCount = 0;
int maxSpinWaitCount = 100;
while (numberOfParallelExecutions > tasksCompletedCount && error is null && spinWaitCount < maxSpinWaitCount))
{
await Task.Delay(TimeSpan.FromMilliseconds(100));
spinWaitCount++;
}
return (error == null, error);
}
Run Code Online (Sandbox Code Playgroud)
这不是最干净的代码,也不是我们的最终结果,但逻辑保持不变。这段代码每次都证明了我们的线程安全错误。
以下是我们如何使用它:
int numberOfParallelExecutions = 2;
RunTaskInParallel(() => doSomeThingAsync(), numberOfParallelExecutions);
Run Code Online (Sandbox Code Playgroud)
希望这可以帮助某人。
我经常编写单元测试来证明某些代码是线程安全的.通常,我会编写这些测试以响应生产中发现的错误.在这种情况下,测试的目的是证明错误被复制(测试失败),并且新代码修复了线程问题(测试通过),然后充当未来版本的回归测试.
我编写的大多数测试线程安全测试测试了一个线程争用情况,但有些也测试了线程死锁.
主动测试代码是线程安全的单元测试有点棘手.不是因为单元测试更难写,而是因为你必须进行可靠的分析以确定(猜测,真的)可能是线程不安全的.如果您的分析是正确的,那么您应该能够编写一个失败的测试,直到您使代码线程安全为止.
在测试线程争用情况时,我的测试几乎总是遵循相同的模式:(这是伪代码)
boolean failed = false;
int iterations = 100;
// threads interact with some object - either
Thread thread1 = new Thread(new ThreadStart(delegate() {
for (int i=0; i<iterations; i++) {
doSomething(); // call unsafe code
// check that object is not out of synch due to other thread
if (bad()) {
failed = true;
}
}
});
Thread thread2 = new Thread(new ThreadStart(delegate() {
for (int i=0; i<iterations; i++) {
doSomething(); // call unsafe code
// check that object is not out of synch due to other thread
if (bad()) {
failed = true;
}
}
});
thread1.start();
thread2.start();
thread1.join();
thread2.join();
Assert.IsFalse(failed, "code was thread safe");
Run Code Online (Sandbox Code Playgroud)
我已经放弃了单元测试来尝试检测线程问题。我只是使用一整套硬件(如果有硬件的话 - 通常我的工作有,网络上的 uController)进行系统负载测试,并且有大量不合理的自动化客户端全速运行。当我做其他事情时,我让它运行一周。周末我会卸下重担,检查还剩下什么。如果它仍然有效,没有对象泄漏并且内存没有明显增加,我就会发送它。
这是我能承受的最高质量了:)
归档时间: |
|
查看次数: |
5334 次 |
最近记录: |