一般情况下使用Thread类创建线程,但创建线程和销毁线程的开销较大,过多的线程会消耗CPU及内存资源。因而.net提供了ThreadPool静态类改善该问题。
//线程池默认下限:1 GetMinThreads()
//线程池默认上限:25 GetMaxThreads()
//设置线程池下限:SetMinThreads()
//设置线程池上限:SetMaxThreads()
using System;
using System.Threading;
namespace ThreadPoolTech
{
class Program
{
/*record the finished thread*/
static int finishThreadCount = 0;
/*record result of each thread*/
static int[] result = new int[10];
static void Main(string[] args)
{
/*insert 9 work thread into thread pool*/
for(var i=1;i<=9;i++)
{
ThreadPool.QueueUserWorkItem(new WaitCallback(WorkFunction), i);
}
/*wait these 9 threads to finish their work*/
while (finishThreadCount < 9) ;
/*output result*/
for(var i=1;i<=9;i++)
{
Console.WriteLine("Thread{0}:{0}!={1}", i, result[i]);
}
}
/*WorkFunction*/
public static void WorkFunction(object n)
{
/*calculate the factorial*/
int fac = 1;
for(int i=1;i<=(int)n;i++)
{
fac *= i;
}
/*record the result*/
result[(int)n] = fac;
/*record the finished thread count*/
finishThreadCount++;
}
}
}
示意实例:--默认下限5,上限25
1. 线程池创建时,自动生成5个空线程;
2. 每排入一个任务,会有一个空线程接手并开始执行;
3. 当任务总量超过下限时,系统等待500ms(看是否有完成的线程)后,如果没有已经完成的线程,则创建新的线程开始执行该任务;
4. 当线程数量达到上限时,继续增加任务时,并不会新增线程。如此时排入了100个任务,则有75个会在池外排队。一旦有线程中任务完成,该线程马上会被排除进入的任务使用。
5. 当任务数量递减小于25时,则完成任务而空闲的线程会先空闲120s,没有新增任务则释放并回收相关资源。直至达到下限值。
6. 当任务全部完成时,系统还是会剩下5个空线程。
但以下三种情况,不能使用线程池:
1. 线程需要执行很长时间;
2. 需要为线程指定单独的优先级;
3. 在执行过程中需要对线程进行操作,比如睡眠或者挂起;
对象池:
当我们频繁创建删除大量对象的时候,对象的创建删除所造成的开销就不容小觑了。为了提高性能,我们往往需要实现一个对象池作为Cache:使用对象时,它从池中提取。用完对象时,它放回池中。从而减少创建对象的开销。
//线程池默认下限:1 GetMinThreads()
//线程池默认上限:25 GetMaxThreads()
//设置线程池下限:SetMinThreads()
//设置线程池上限:SetMaxThreads()
using System;
using System.Threading;
using System.Collections.Concurrent;
using System.Diagnostics.Contracts;
namespace ThreadPoolTech
{
/*对象池*/
public class ObjectPool<T>
{
ConcurrentBag<T> buffer;
Func<T> createFunc;
Action<T> resetFunc;
public ObjectPool(Func<T> createFunc, Action<T> resetFunc, int capacity)
{
Contract.Assume(createFunc != null);
Contract.Assume(capacity > 0);
this.buffer = new ConcurrentBag<T>();
this.createFunc = createFunc;
this.resetFunc = resetFunc;
this.Capacity = capacity;
}
public int Capacity { get; private set; }
public int Count { get { return buffer.Count; } }
/// <summary>
/// 申请对象
/// </summary>
public T GetObject()
{
var obj = default(T);
if (!buffer.TryTake(out obj))
return createFunc();
else
return obj;
}
/// <summary>
/// 释放对象
/// </summary>
public void PutObject(T obj)
{
Contract.Assume(obj != null);
if (Count >= Capacity) //超过容量了,不再需要
return;
if (resetFunc != null)
resetFunc(obj);
buffer.Add(obj);
}
}
}
ConcurrentBag<T>对于同一个线程值的添加和删除是非常快的,因为ConcurrentBag内部将数据按线程的标识而独立存储,所以一个线程从自己的数据中移除一个数据是非常快的,当然如果这个线程中没有数据,那么只能从其他线程中移除数据,此时会发生一些性能损耗从而确保线程安全!
//+ using System.Threading.Tasks;
//+ using System.Collections.Concurrent;
var bag = new ConcurrentBag<string>();
var t1 = Task.Factory.StartNew(() =>
{
bag.Add("线程1: 1");
Thread.Sleep(1000);
bag.Add("线程1: 2");
foreach (var str in bag)
Console.WriteLine(str);
});
var t2 = Task.Factory.StartNew(() =>
{
bag.Add("线程2: 1");
Thread.Sleep(2000);
string str;
bag.TryTake(out str);
Console.WriteLine("线程2取出:" + str);
});
Task.WaitAll(t1, t2);
/*输出*/
线程1: 2
线程1: 1
线程2: 1
线程2取出:线程2: 1
浙公网安备 33010602011771号