如何对线程安全队列进行单元测试

5

我需要一个简单的数据结构,它需要满足以下要求:

  • 它应该像队列一样运作,
  • 所有入队操作都应该是原子性的。

我对多线程有非常有限的经验,但这就是我想到的:

public class Tickets
{
    private ConcurrentQueue<uint> _tickets;

    public Tickets(uint from, uint to)
    {
        Initialize(from, to);
    }

    private readonly object _lock = new object();
    public void Initialize(uint from, uint to)
    {
        lock(_lock)
        {
            _tickets = new ConcurrentQueue<uint>();

            for (uint i = from; i <= to; i++)
            {
                _tickets.Enqueue(i);
            }
        }
    }

    public uint Dequeue()
    {
        uint number;
        if (_tickets.TryDequeue(out number))
        {
            return number;
        }

        throw new ArgumentException("Ticket queue empty!");
    }
}

第一个问题:这段代码可行吗?

第二个问题:如何对此类进行单元测试(例如,使用两个线程定期在具有元素(1、2、3、4、5、6)的队列上执行出队操作,第一个线程应仅获取奇数,第二个线程应仅获取偶数)?我尝试过这样做,但断言没有执行:

[Test]
public void Test()
{
    var tickets = new Tickets(1, 4);
    var t1 = new Thread(() =>
                            {
                                Assert.AreEqual(1, tickets.Dequeue());
                                Thread.Sleep(100);
                                Assert.AreEqual(3, tickets.Dequeue());
                            });


    var t2 = new Thread(() =>
                            {
                                Assert.AreEqual(2, tickets.Dequeue());
                                Thread.Sleep(100);
                                Assert.AreEqual(4, tickets.Dequeue());
                            });

    t1.Start();
    t2.Start();
}

第一个问题的答案:你的集合不是线程安全的,因为在初始化时可能会调用Dequeue - Ahmed KRAIEM
1
需要在你的“Dequeue”周围加锁。而且,认为两个线程会理想地交错执行是有缺陷的概念。不能保证它们一定会像你提供的代码那样轮流执行。 - weston
@weston:我也这么认为,所以我问怎样以适当的方式进行单元测试。 - sventevit
3
你不使用并发框架队列的原因是什么?http://msdn.microsoft.com/en-us/library/system.collections.concurrent.aspx - penguat
我不知道有什么方法可以通过自动化测试彻底测试某个东西的线程安全性,尽管 Pex 可能可以做到:http://research.microsoft.com/en-us/projects/pex/ - penguat
1
他们以“轮询”的方式从队列中取出数据是否很重要,还是只是为了测试的目的?也就是说,在实际环境中,如果线程1在线程2之前先取出1和2,你是否关心这个问题? - weston
2个回答

5
我会使用CHESS:http://research.microsoft.com/en-us/projects/chess CHESS是一款用于查找和重现并发程序中的Heisenbugs的工具。CHESS反复运行并发测试,确保每次运行都采用不同的交织方式。如果某个交织方式导致错误,CHESS可以重现交织以进行更好的调试。CHESS可用于托管程序和本机程序。

4
多线程和单元测试的问题在于时间。当您尝试将多个线程引入单元测试时,您可能会遇到无法重现的测试结果,有时测试通过,而有时则不通过。
但是,仅仅为了解释为什么您的断言可能不会执行,单元测试在线程之前完成。它需要等待线程完成,而不仅仅是启动线程并继续执行。还有可能单元测试框架本身不是线程安全的,或者不能从其他线程调用Asserts。
很抱歉我没有解决方案,但我也不知道任何针对多线程代码的自动化测试解决方案。
另请参见:如何对多线程代码进行单元测试?

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接