多线程并发测试类库

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

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

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

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
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; }
}

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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方法的重载,一般情况下能满足我们的多线程并发测试场景了。

 

 

另一地址:http://blog.moozi.net/archives/multi-threaded-concurrent-test-library.html

关注更多:http://blog.moozi.net

posted @ 2011-04-16 20:21  木子博客  阅读(8603)  评论(13编辑  收藏  举报