1. 程式人生 > 程式設計 >.NET Core 中物件池 Object Pool的使用

.NET Core 中物件池 Object Pool的使用

目錄
  • 一、什麼是物件池
  • 二、.NET Core 中的物件池
  • 三、本文小結

一、什麼是物件池

物件池簡單來說就是一種為物件提供可複用能力的軟體設計思路。我們常說有借有還,再借不難,而物件池就是通過借和還這樣兩個動作來保證物件可以被重複使用,從而節省頻繁建立物件的效能開銷。物件池最常用的場景是遊戲設計,因為在遊戲中大量存在著可複用的物件,源源不斷的子彈出現並不是迴圈再生的。在中存在著被稱為連線池的東西,每當出現數據庫無法連線的情況時,經驗豐富的開發人員往往會先檢查連線池是否滿了,這其實就是物件池模式在特定領域的具體實現。因此物件池本質上就是負責一組物件建立和銷燬的容器。 物件池最大的優勢是可以自主地管理池子內的每個物件,決定它們是需要被回收還是可以重複使用。我們都知道建立一個新物件需要消耗一定的系統資源,一旦這些物件可以重複地使用就可以節省系統資源開銷,這對提高系統性能會非常有幫助。下面的程式碼實微軟官方文件實現的一個簡單的物件池:


public class ObjectPool<T> : IObjectPool<T>

{

	private Func<T> _instanceFactory;

	private ConcurrentBag<T> _instanceItems;

	public ObjectPool(Func<T> instanceFactory)

	{

		_instanceFactory = instanceFactory ?? 

		throw new ArgumentNullException(nameof(instanceFactory));

		_instanceItems = new ConcurrentBag<T>();

	}

	public T Get()

	{

		T item;

		if (_instanceItems.TryTake(out item)) return item;

		return _instanceFactory();

	}

	public void Return(T item)

	{

		_instanceItems.Add(item);

	}

}

二、.NET Core 中的物件池

.NET Core 中微軟已經為我們提供了物件池的實現,即Microsoft.Extensions.ObjectPool。它主要提供了三個核心的元件,分別是ObjectPoolObjectPoolProviderIPooledObjectPolicyObjectPool是一個抽象類,對外提供了Get和Return兩個方法,這就是所謂的有借有還。ObjectPoolProvider同樣是一個抽象類,它的職責就是建立ObjectPool,它提供了兩個Create方法,兩者的區別是無引數版本本質上使用的是DefaultPooledObjectPolicy

。它和DefaultObjectPool、DefaultObjectPoolProvider都是微軟提供的預設實現,IPooledObjectPolicy可以為不同的物件池定義不同的策略,來決定物件如何借、是否可以還。DefaultObjectPool內部使用ObjectWrapper[]來管理物件,ObjectWrapper[]的大小等於 maximumRetained-1,預設情況下maximumRetained等於Environment.ProcessorCount * 2,這裡主要用到了Interlocked.CompareExchange()方法,

具體程式碼如下:

public override T Get()

{

  var item = _firstItem;

  if (item == null || Interlocked.CompareExchange(ref _firstItem,null,item) != item)

  {

    var items = _items;

    for (var i = 0; i < items.Length; i++)

    {

      item = items[i].Element;

      if (item != null && Interlocked.CompareExchange(ref items[i].Element,item) == item)

      {

        return item;

      }

    }

    item = Create();

  }

  return item;

}

// Non-inline to improvehttp://www.cppcns.com its code quality as uncommon path

[MethodImpl(MethodImplOptions.NoInlining)]

private T Create() => _fastPolicy?.Create() ?? _policy.Create();



public override void Return(T obj)

{

  if (_isDefaultPolicy || (_fastPolicy?.Return(obj) ?? _policy.Return(obj)))

  {

    if (_firstItem != null || Interlocked.CompareExchange(ref _firstItem,obj,null) != null)

    {

      var items = _items;

      for (var i = 0; i < items.Length && Interlocked.CompareExchange(ref items[i].Element,null) != null; ++i)

      {

      }

    }

  }

}

這裡用到Interlocked.CompareExchange()方法,Get()方法將items[i].Elementnull進行交換,將指定元素設為 null 並返回原始值。Return()方法將items[i].Element和obj交換後的值不為 null,表示指定元素已經歸還,這個方法只有在第一個引數和第三個引數相等時才會發生交換。

說了這麼多,我們來看一下物件池具體的用法:

var service = new ServiceCollection();

//使用DefaultObjectPoolProvider

service.AddSingleton<ObjectPoolProvider,DefaultObjectPoolProvider>();

//使用預設策略

service.AddSingleton<ObjectPool<Foo>>(serviceProvider =>

{

  var objectPoolProvider = serviceProvider.GetRequiredService<ObjectPoolProvider>();

  return objectPoolProvider.Create<Foo>();

});

//使用自定義策略

service.AddSingleton<ObjectPool<Foo>>(serviceProvider =>

http://www.cppcns.com{

  var objectPoolProvider = serviceProvider.GetRequiredService<ObjectPoolProvider>();

  return objectPoolProvider.Create(new FooObjectPoolPolicy());

});



var serviceProvider = _service.BuildServiceProvider();



var objectPool = _serviceProvider.GetService<ObjectPool<Foo>>();



//有借有還,兩次是同一個物件

var item1 = objectPool.Get();

objectPool.Return(item1);

var item2 = objectPool.Get();

Assert.AreEqual(item1,item2);//true



//有借無還,兩次是不同的物件

var item3 = objectPool.Get();

var item4 = objectPool.Get();

Assert.AreEqual(item3,item4);//false

上面的程式碼中Foo和FooObjectPoolPolicy是兩個工具類:

public class Foo

{

  public string Id { get; set; }

  public DateTime? CreatedAt { get; set; }

  public string CreatedBy { get; set; }

}



public class FooObjectPoolPolicy : IPooledObjectPolicy<Foo>

{

  public Foo Create()

  {

    return new Foo()

    {

      Id = Guid.NewGuid().ToString("N"),CreatedAt = DateTime.Now,CreatedBy = "zs"

    };

  }



  public bool Return(Foo obj)

  {

    return true;

  }

}

TIP:當你需要控制物件池內的物件如何被建立的時候,你可以考慮實現自定義的IPooledObjectPolicy<T>,反之DefaultPooledObjectPolicy<T>實現完全可以滿足你的使用。

三、本文小結

實現物件池可以考慮ConcurrentBag、Stack、Queue以及BlockingCollection等多種資料結構,而微軟在.NET Core 中已經為我們實現了一個簡單的物件池,大多數情況下,我們只需要定義自己的IPooledObjectPolicy去決定物件應該怎麼樣借、怎麼樣還。總之遊戲世界裡的 GameObject、資料庫裡的連線池,都是物件池模式在各自領域中的具體實現。

TIP:物件池是一種通過複用物件來減少資源開銷進而實現提高系統性能的軟體設計模式,其核心是控制容器內物件的生命週期來規避系統的主動回收,從物件池中借出的物件必須要及時歸還,否則會造成物件池中沒有可用資源。

到此這篇關於 .NET Core 中物件池 Object Pool的使用的文章就介紹到這了,更多相關 .NET Core 中物件池 內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!