对于CLR的线程池的概念请看:
本篇内容主要讨论CLR线程池的边缘情况。
一、对工作者线程进行测试。
测试场景1:
设置工作者线程的下限数量5,上限数量10,给线程池一瞬间安排20个任务,每个任务占用线程时间18秒,然后观察线程池在对待线程数量的上下限的反应,以及完成一个任务之后返回线程池中的线程之后的情况。
测试结果:
结论:
1. 运行的工作者线程数量在下限以下,会尽可能多立即执行任务。(前5行)
2. 线程数量每秒不超过2个的频率创建线程。(6-10行)
3. 运行的工作者线程数量到达上限,会停止创建新线程。(10行)
4. 在旧任务执行完毕后,新任务立即执行。(11-20行)
5. 线程并不“属于”任何一个任务,或者说任务并不“拥有”线程。
6. 任务只是借用一个线程来做事,完成以后便会还回。
测试1代码:
ThreadPool.SetMinThreads(5, 5);
ThreadPool.SetMaxThreads(10, 10);
Console.WriteLine("--Only worker threads testing start--");
Stopwatch watch = new Stopwatch();
watch.Start();
WaitCallback callback = index =>
{
Console.WriteLine(String.Format("Task {0} started;Seconds {1}", index, watch.Elapsed.TotalSeconds));
double sleepDuring = 18000 - Math.Floor(watch.Elapsed.TotalSeconds) * 1000;
Thread.Sleep((int)sleepDuring);
};
for (int i = 1; i <= 20; i++) {
ThreadPool.QueueUserWorkItem(callback, i);
}
Console.ReadKey();
Console.WriteLine("--Only worker threads testing end--");
Console.ReadKey();
二、对异步IO请求进行测试。
测试场景2:
设置工作者线程的下限数量5,上限数量10。
IO完成线程的下限数量5,上限数量10。
给线程池一瞬间安排15个异步IO任务,每个任务占用线程时间10秒,然后发送异步IO请求,之后观察线程池在对待线程数量的上下限的反应。
测试结果:
结论:
1. 运行的工作者线程数量在下限以下,会尽可能多立即执行任务。(前5行)
2. 线程数量每秒不超过2个的频率创建线程。(6-10行)
3. 运行的工作者线程数量到达上限,会停止创建新线程。(10行)
4. 工作者线程数量到达上限,任何一个工作者线程进行异步IO请求就会报Exception。
5. 工作线程和IO完成线程在线程池里面完全独立,互不影响。
测试场景3:
设置工作者线程的下限数量5,上限数量20。
IO完成线程的下限数量5,上限数量10。
给线程池一瞬间安排15个异步IO任务,每个任务占用线程时间10秒,然后发送异步IO请求,之后观察线程池在对待线程数量的上下限的反应。
测试数据:
当线程池有足够的工作者线程时,可以正常调用异步IO请求。当我们使用IO异步优化服务器的时候,必须要保证CLR线程池的工作者线程不能被100%占用,要保证有多余的工作者线程,这样才能正常工作。
测试场景4:
设置工作者线程的下限数量5,上限数量30,IO完成线程的下限数量5,上限数量10,给线程池一瞬间安排20个异步IO任务,然后异步IO请求回调中占用IO线程10秒。观察线程池中IO线程的上下限反应。
测试结果:
结论:
1. 运行的IO线程数量在下限以下,会尽可能多立即执行IO回调任务。(21-25行)
2. IO线程数量每秒不超过2个的频率创建线程。(26-30行)
3. 运行的IO线程数量到达上限,会停止创建新线程。(30行)
4. 在旧任务执行完毕后,新任务立即执行。(31-40行)
5. 线程并不“属于”任何一个任务,或者说任务并不“拥有”线程。
6. 任务只是借用一个线程来做事,完成以后便会还回。
测试代码:
class Program
{
static void Main(string[] args)
{
ThreadPool.SetMinThreads(30, 5);
ThreadPool.SetMaxThreads(30, 10);
Console.WriteLine("--Worker threads and completion port threads testing start--");
Stopwatch watch = new Stopwatch();
watch.Start();
WaitCallback callback = index => {
Console.WriteLine(String.Format("Task {0} started; Seconds {1}", index, watch.Elapsed.TotalSeconds));
IOCP_Handle(index, watch);
};
for (int i = 1; i <= 20; i++) {
ThreadPool.QueueUserWorkItem(callback, i);
}
Console.ReadKey();
Console.WriteLine("--Worker threads and completion port threads testing end--");
Console.ReadKey();
}
private static void IOCP_Handle(object index, Stopwatch watch)
{
WebRequest request = HttpWebRequest.Create(string.Format("https://www.ef.com/Common/css/fonts.css?v={0}", index.ToString()));
CallBackParam cb = new CallBackParam()
{
Index = (int)index,
Request = request,
Watch = watch
};
try
{
request.BeginGetResponse(HandleAsyncCallback, cb);
}
catch (Exception ex)
{
Console.WriteLine(string.Format("Task {0} BeginGetResponse; Seconds {1} \r\n {2}", index.ToString(), watch.Elapsed.TotalSeconds, ex.Message));
}
}
static void HandleAsyncCallback(IAsyncResult ar)
{
CallBackParam cb = (CallBackParam)ar.AsyncState;
string index = cb.Index.ToString();
Console.WriteLine(String.Format("Task {0} finished; Seconds {1}", index, cb.Watch.Elapsed.TotalSeconds));
try
{
WebResponse response = cb.Request.EndGetResponse(ar);
response.Close();
}
catch (Exception ex)
{
Console.WriteLine(string.Format("Task {0} EndGetResponse. \r\n {1}", index.ToString(), ex.Message));
}
finally
{
cb.Request.Abort();
}
double sleepDuring = 10000 - Math.Floor(cb.Watch.Elapsed.TotalSeconds) * 1000;
Thread.Sleep((int)sleepDuring);
}
}
public class CallBackParam
{
public WebRequest Request { get; set; }
public int Index { get; set; }
public Stopwatch Watch { get; set; }
}