1. 程式人生 > >ASP.NET Core 中的 ObjectPool 物件重用(一)

ASP.NET Core 中的 ObjectPool 物件重用(一)

前言

物件池是一種設計模式,一個物件池包含一組已經初始化過且可以使用的物件,而可以在有需求時建立和銷燬物件。池的物件可以從池中取得物件,對其進行操作處理,並在不需要時歸還給池子而非直接銷燬他,他是一種特殊的工廠物件。
若初始化、例項化的代價高,且有需求需要經常例項化,但每次例項化的數量較小的情況下,使用物件池可以過得顯著的效能提升。從池子中取得物件的時間是可測的,但新建一個實際所需要的時間是不確定的。

物件池的優勢

說到池我們就會聯想到很多的概念,如執行緒池、資料庫連線池、記憶體池等等在多執行緒設計中可以通過池化機制來進行物件的複用從而提高效能。池的核心優勢是 物件複用,這樣就免去了物件建立的開銷以及回收產生的內容開銷,尤其建立物件這是一個很耗時的事情比如IO操作.

拿我們最常見的執行緒池為例,執行緒這個物件是可以複用的,程式要執行的任務,這些任務可以交給複用的執行緒來處理,而執行緒池建立恰恰又是一個比較耗時的操作,我們通過執行緒物件的池化技術達到複用執行緒的目的。

ConcurrentBag實現物件池

池化中需要注意的是多執行緒中保證執行緒安全,.NET Framework 4 引入了 System.Collections.Concurrent 名稱空間,其中包含多個執行緒安全且可縮放的集合類。 多個執行緒可以安全高效地從這些集合新增或刪除項,而無需在使用者程式碼中進行其他同步。 編寫新程式碼時,只要將多個執行緒同時寫入到集合時,就使用併發集合類。

ObjectPool

  • Get方法用於從物件池獲取到可用物件,如果物件不可用則建立物件並返回出來
  • Return方法使用者將物件返回到物件池

    public class ObjectPool<T>
    {
        private ConcurrentBag<T> _object;
        private Func<T> _objectGenerator;

        public ObjectPool(Func<T> objectGenerator) {
            _object = new ConcurrentBag<T>();
            _objectGenerator = objectGenerator;
        }
        /// <summary>
        ///     取出
        /// </summary>
        /// <returns></returns>
        public T CheckOut() {
            T item;
            if (_object.TryTake(out item)) return item;
            return _objectGenerator();
        }
        /// <summary>
        ///     歸還
        /// </summary>
        /// <param name="obj"></param>
        public void CheckIn(T obj) {
            _object.Add(obj);
        }

    }

測試


    class Program
    {
        static void Main(string[] args)
        {
            CancellationTokenSource cts = new CancellationTokenSource();

            // Create an opportunity for the user to cancel.
            Task.Run(() =>
            {
                if (Console.ReadKey().KeyChar == 'c' || Console.ReadKey().KeyChar == 'C')
                    cts.Cancel();
            });

            ObjectPool<MyClass> pool = new ObjectPool<MyClass>(() => new MyClass());

            // Create a high demand for MyClass objects.
            Parallel.For(0, 1000000, (i, loopState) =>
            {
                MyClass mc = pool.CheckOut();
                Console.CursorLeft = 0;
                // This is the bottleneck in our application. All threads in this loop
                // must serialize their access to the static Console class.
                Console.WriteLine("{0:####.####}", mc.GetValue(i));

                pool.CheckIn(mc);
                if (cts.Token.IsCancellationRequested)
                    loopState.Stop();

            });
            Console.WriteLine("Press the Enter key to exit.");
            Console.ReadLine();
            cts.Dispose();
        }

        class MyClass
        {
            public int[] Nums { get; set; }
            public double GetValue(long i)
            {
                return Math.Sqrt(Nums[i]);
            }
            public MyClass()
            {
                Nums = new int[1000000];
                Random rand = new Random();
                for (int i = 0; i < Nums.Length; i++)
                    Nums[i] = rand.Next();
            }
        }


    }

這是一個簡單的物件池實現,在實際的場景中還需要考慮最小值,最大值,異常處理等等

總結

在建立資源時會消耗一定的系統資源,尤其在及其複雜的結構中效果相對來說是挺明顯的,再加上頻繁的建立,例項化消耗的資源是很昂貴的.物件池對這些提成是相當有幫助的.

並非任何情況下都需要使用物件池,在複用生成某種物件的操作成為影響效能因素的時候,才適合採用物件池。如果物件池提成效能提高並不重要的話,還是建議不採用物件池,保持程式碼簡單.

參考

https://zh.m.wikipedia.org/zh-cn/%E5%AF%B9%E8%B1%A1%E6%B1%A0%E6%A8%A1%E5%BC%8F

https://docs.microsoft.com/en-us/dotnet/standard/collections/thread-safe/how-to-create-an-object-pool