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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM