多线程并发测试类库

WEB项目中除了单元测试,还经常需要多线程测试一个方法是否存在并发问题,或者是否有性能问题。每次都要写测试代码总是一件很累的事情。于是写了这一个多线程测试的类库,用来进行快速的多线程并发测试。

多线程并发测试时,需要等所有线程测试结束后通知主线程,主线程才能进行下一步动作,这里主要用到了ManualResetEvent。ManualResetEvent 类表示一个本地等待处理事件,在已发事件信号后必须手动重置该事件。通常,此通信涉及一个线程在其他线程进行之前必须完成的任务。当一个线程开始一个活动(此活动必须完成后,其他线程才能开始)时,它调用 Reset 以将 ManualResetEvent 置于非终止状态。此线程可被视为控制 ManualResetEvent。调用 ManualResetEvent 上的 WaitOne 的线程将阻止,并等待信号。当控制线程完成活动时,它调用 Set 以发出等待线程可以继续进行的信号。并释放所有等待线程。一旦它被终止,ManualResetEvent 将保持终止状态,直到它被手动重置。即对 WaitOne 的调用将立即返回。可以通过将布尔值传递给构造函数来控制 ManualResetEvent 的初始状态,如果初始状态处于终止状态,为 true;否则为 false。

多线程并发测试由以下步骤完成:

  1. 创建并发测试的线程数,先创建的线程等待最后一个线程创建完成。
  2. 所有线程执行待测试的方法,返回测试的结果。
  3. 等所有线程执行完成后,进入思考时间等待。
  4. 继续进行循环测试。

我们来看这个多线程并发测试的代码。

    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Threading;
    /// <summary>
    /// 并发测试
    /// </summary>
    public class ConcurrentTest : IDisposable
    {
        #region 私有方法
        /// <summary>
        /// 测试方法所在的接口
        /// </summary>
        private Func<bool> func;
        /// <summary>
        /// 主线程控制信号
        /// </summary>
        private ManualResetEvent manualResetEvent;
        /// <summary>
        /// 测试线程控制信号
        /// </summary>
        private ManualResetEvent threadResetEvent;
        /// <summary>
        /// 待执行的线程数
        /// </summary>
        private List<int> threads;
        /// <summary>
        /// 测试结果
        /// </summary>
        private List<ConcurrentTestResult> results;
        /// <summary>
        /// 执行测试的成功数
        /// </summary>
        private int successCount;
        /// <summary>
        /// 执行测试的失败数
        /// </summary>
        private int failureCount;
        /// <summary>
        /// 测试耗时
        /// </summary>
        private long elapsedMilliseconds;
        /// <summary>
        /// 当前线程
        /// </summary>
        private int currentIndex;
        /// <summary>
        /// 当前测试的总线程数
        /// </summary>
        private int currentCount;
        /// <summary>
        /// 思考时间
        /// </summary>
        private int thinkTime;
        /// <summary>
        /// 重复次数
        /// </summary>
        private int repeatCount;
        /// <summary>
        /// 测试计时器
        /// </summary>
        private Stopwatch stopwatch;
        #endregion

        #region 构造函数
        /// <summary>
        /// 构造函数
        /// </summary>
        public ConcurrentTest()
        {
            manualResetEvent = new ManualResetEvent(true);
            threadResetEvent = new ManualResetEvent(true);
            stopwatch = new Stopwatch();
        }
        #endregion

        #region 执行测试
        /// <summary>
        /// 执行多线程测试
        /// </summary>
        /// <param name="threadCount">需要测试的线程数</param>
        /// <param name="func">待执行方法</param>
        /// <returns></returns>
        public List<ConcurrentTestResult> Execute(int threadCount, Func<bool> func)
        {
            return Execute(threadCount, 1, func);
        }
        /// <summary>
        /// 执行多线程测试
        /// </summary>
        /// <param name="threadCount">需要测试的线程数</param>
        /// <param name="repeatCount">重复次数</param>
        /// <param name="func">待执行方法</param>
        /// <returns></returns>
        public List<ConcurrentTestResult> Execute(int threadCount, int repeatCount, Func<bool> func)
        {
            return Execute(threadCount, 0, repeatCount, func);
        }
        /// <summary>
        /// 执行多线程测试
        /// </summary>
        /// <param name="threadCount">需要测试的线程数</param>
        /// <param name="thinkTime">思考时间,单位毫秒</param>
        /// <param name="repeatCount">重复次数</param>
        /// <param name="func">待执行方法</param>
        /// <returns></returns>
        public List<ConcurrentTestResult> Execute(int threadCount, int thinkTime, int repeatCount, Func<bool> func)
        {
            return Execute(new List<int>() { threadCount }, thinkTime, repeatCount, func);
        }
        /// <summary>
        /// 执行多线程测试
        /// </summary>
        /// <param name="threads">分别需要测试的线程数</param>
        /// <param name="thinkTime">思考时间,单位毫秒</param>
        /// <param name="repeatCount">重复次数</param>
        /// <param name="func">待执行方法</param>
        /// <returns></returns>
        public List<ConcurrentTestResult> Execute(List<int> threads, int thinkTime, int repeatCount, Func<bool> func)
        {
            this.func = func;
            this.threads = threads;
            this.thinkTime = thinkTime;
            this.repeatCount = repeatCount;
            CheckParameters();
            CreateMultiThread();
            return this.results;
        }
        #endregion

        #region 验证参数
        /// <summary>
        /// 验证参数
        /// </summary>
        private void CheckParameters()
        {
            if (func == null) throw new ArgumentNullException("func不能为空");
            if (threads == null || threads.Count == 0) throw new ArgumentNullException("threads不能为空或者长度不能为0");
            if (thinkTime < 0) throw new Exception("thinkTime不能小于0");
            if (repeatCount <= 0) throw new Exception("repeatCount不能小于等于0");
        }
        #endregion

        #region 创建多线程并执行测试
        /// <summary>
        /// 创建多线程进行测试
        /// </summary>
        private void CreateMultiThread()
        {
            results = new List<ConcurrentTestResult>(threads.Count);
            foreach (int threadCount in threads)
            {
                for (int repeat = 0; repeat < repeatCount; repeat++)
                {
                    //主线程进入阻止状态
                    manualResetEvent.Reset();
                    //测试线程进入阻止状态
                    threadResetEvent.Reset();
                    stopwatch.Reset();
                    currentCount = threadCount;
                    currentIndex = 0;
                    successCount = 0;
                    failureCount = 0;
                    elapsedMilliseconds = 0;
                    for (int i = 0; i < currentCount; i++)
                    {
                        Thread t = new Thread(new ThreadStart(DoWork));
                        t.Start();
                    }
                    //阻止主线程,等待测试线程完成测试
                    manualResetEvent.WaitOne();
                    results.Add(new ConcurrentTestResult()
                    {
                        FailureCount = failureCount,
                        SuccessCount = successCount,
                        ElapsedMilliseconds = elapsedMilliseconds
                    });
                    Thread.Sleep(thinkTime);
                }
            }
        }
        /// <summary>
        /// 执行测试方法
        /// </summary>
        private void DoWork()
        {
            bool executeResult;
            Interlocked.Increment(ref currentIndex);
            if (currentIndex < currentCount)
            {
                //等待所有线程创建完毕后同时执行测试
                threadResetEvent.WaitOne();
            }
            else
            {
                //最后一个线程创建完成,通知所有线程,开始执行测试
                threadResetEvent.Set();
                //开始计时
                stopwatch.Start();
            }
            //执行测试
            executeResult = func();
            Interlocked.Decrement(ref currentIndex);
            if (currentIndex == 0)
            {
                //最后一个线程执行的测试结束,结束计时
                stopwatch.Stop();
                elapsedMilliseconds = stopwatch.ElapsedMilliseconds;
                //保存测试结果
                if (executeResult)
                    Interlocked.Increment(ref successCount);
                else
                    Interlocked.Increment(ref failureCount);
                //通知主线程继续
                manualResetEvent.Set();
            }
            else
            {
                //保存测试结果
                if (executeResult)
                    Interlocked.Increment(ref successCount);
                else
                    Interlocked.Increment(ref failureCount);
            }
        }
        #endregion

        #region 释放资源
        /// <summary>
        /// 释放资源
        /// </summary>
        public void Dispose()
        {
            manualResetEvent.Close();
            threadResetEvent.Close();
        }
        #endregion
    }
    /// <summary>
    /// 并发测试结果
    /// </summary>
    public class ConcurrentTestResult
    {
        /// <summary>
        /// 当前执行线程总数
        /// </summary>
        public int ThreadCount
        {
            get { return SuccessCount + FailureCount; }
        }
        /// <summary>
        /// 测试成功数
        /// </summary>
        public int SuccessCount { get; set; }
        /// <summary>
        /// 测试失败数
        /// </summary>
        public int FailureCount { get; set; }
        /// <summary>
        /// 总耗时
        /// </summary>
        public long ElapsedMilliseconds { get; set; }
    }

使用起来就非常简单了,我们看测试代码:

    class Program
    {
        static void Main(string[] args)
        {
            using (ConcurrentTest concurrentTest = new ConcurrentTest())
            {
                var result = concurrentTest.Execute(5, -1, 10, new TestClass().Execute);
                foreach (var item in result)
                {
                    Console.WriteLine("线程数:{0}\t成功:{1}\t失败:{2}\t耗时:{3}",
                        item.ThreadCount, item.SuccessCount, item.FailureCount, item.ElapsedMilliseconds);

                }
            }
            Console.ReadKey(true);
        }
    }

    public class TestClass
    {
        public bool Execute()
        {
            int tempValue = GetRandom();
            System.Threading.Thread.Sleep(tempValue);
            return tempValue % 2 == 0;
        }
        private int GetRandom()
        {
            return new Random().Next(990, 1000);
        }
    }

测试类库提供了4个Execute方法的重载,一般情况下能满足我们的多线程并发测试场景了。

已有 6 条评论

  1. 沃尔沃 说:

    不是很懂,了解下,

  2. 宁波美术培训 说:

    不明白,测试不行,还要再研究一下

  3. Loneys 说:

    中文有错别字哈~

    耗秒

    • 木子 说:

      谢谢指正,已更正。

  4. Loneys 说:

    楼主,请问是否废弃了博客园呢?

    • 木子 说:

      没有废弃博客园哈

发表评论

*

:wink: :-| :-x :twisted: :) 8-O :( :roll: :-P :oops: :-o :mrgreen: :lol: :idea: :-D :evil: :cry: 8) :arrow: :-? :?: :!: