参考:线程相关

一般情况下使用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