欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

.NET Core對(duì)象池的應(yīng)用:編程篇

 更新時(shí)間:2021年09月01日 17:04:18   作者:Artech  
對(duì)象池就是對(duì)象的容器,旨在優(yōu)化資源的使用,通過(guò)在一個(gè)容器中池化對(duì)象,并根據(jù)需要重復(fù)使用這些池化對(duì)象來(lái)滿(mǎn)足性能上的需求。這篇文章主要介紹了.NET Core對(duì)象池的應(yīng)用,感興趣的小伙伴可以參考一下

借助于有效的自動(dòng)化垃圾回收機(jī)制,.NET讓開(kāi)發(fā)人員不在關(guān)心對(duì)象的生命周期,但實(shí)際上很多性能問(wèn)題都來(lái)源于GC。并不說(shuō).NET的GC有什么問(wèn)題,而是對(duì)象生命周期的跟蹤和管理本身是需要成本的,不論交給應(yīng)用還是框架來(lái)做,都會(huì)對(duì)性能造成影響。在一些對(duì)性能比較敏感的應(yīng)用中,我們可以通過(guò)對(duì)象復(fù)用的方式避免垃圾對(duì)象的產(chǎn)生,進(jìn)而避免GC因?qū)ο蠡厥諏?dǎo)致的性能損失。對(duì)象池是對(duì)象復(fù)用的一種常用的方式。.NET提供了一個(gè)簡(jiǎn)單高效的對(duì)象池框架,并使用在ASP.NET自身框架中。這個(gè)對(duì)象池狂框架由“Microsoft.Extensions.ObjectPool”這個(gè)NuGet包提供,我們可以通過(guò)添加這個(gè)NuGet包它引入我們的應(yīng)用中。接下來(lái)我們就通過(guò)一些簡(jiǎn)單的示例來(lái)演示一下對(duì)象池的基本編程模式。

一、對(duì)象的借與還

和絕大部分的對(duì)象池編程方式一樣,當(dāng)我們需要消費(fèi)某個(gè)對(duì)象的時(shí)候,我們不會(huì)直接創(chuàng)建它,而是選擇從對(duì)象池中“借出”一個(gè)對(duì)象。一般來(lái)說(shuō),如果對(duì)象池為空,或者現(xiàn)有的對(duì)象都正在被使用,它會(huì)自動(dòng)幫助我們完成對(duì)象的創(chuàng)建。借出的對(duì)象不再使用的時(shí)候,我們需要及時(shí)將其“歸還”到對(duì)象池中以供后續(xù)復(fù)用。我們?cè)谑褂?NET的對(duì)象池框架時(shí),主要會(huì)使用如下這個(gè)ObjectPool<T>類(lèi)型,針對(duì)池化對(duì)象的借與還體現(xiàn)在它的GetReturn方法中。

public abstract class ObjectPool<T> where T: class
{
    public abstract T Get();
    public abstract void Return(T obj);
}

我們接下來(lái)利用一個(gè)簡(jiǎn)單的控制臺(tái)程序來(lái)演示對(duì)象池的基本編程模式。在添加了針對(duì)“Microsoft.Extensions.ObjectPool”這個(gè)NuGet包的引用之后,我們定義了如下這個(gè)FoobarService類(lèi)型來(lái)表示希望池化復(fù)用的服務(wù)對(duì)象。如代碼片段所示,F(xiàn)oobarService具有一個(gè)自增整數(shù)表示Id屬性作為每個(gè)實(shí)例的唯一標(biāo)識(shí),靜態(tài)字段_latestId標(biāo)識(shí)當(dāng)前分發(fā)的最后一個(gè)標(biāo)識(shí)。

public class FoobarService
{
    internal static int _latestId;
    public int Id { get; }
    public FoobarService() => Id = Interlocked.Increment(ref _latestId);
}

通過(guò)對(duì)象池的方式來(lái)使用FoobarService對(duì)象體現(xiàn)在如下的代碼片段中。我們通過(guò)調(diào)用ObjectPool類(lèi)型的靜態(tài)方法Create<FoobarService>方法得到針對(duì)FoobarService類(lèi)型的對(duì)象池,這是一個(gè)ObjectPool<FoobarService>對(duì)象。針對(duì)單個(gè)FoobarService對(duì)象的使用體現(xiàn)在本地方法ExecuteAsync中。如代碼片段所示,我們調(diào)用ObjectPool<FoobarService>對(duì)象的Get方法從對(duì)象池中借出一個(gè)Foobar對(duì)象。為了確定對(duì)象是否真的被復(fù)用,我們?cè)诳刂婆_(tái)上打印出對(duì)象的標(biāo)識(shí)。我們通過(guò)延遲1秒鐘模擬針對(duì)服務(wù)對(duì)象的長(zhǎng)時(shí)間使用,并在最后通過(guò)調(diào)用ObjectPool<FoobarService>對(duì)象的Return方法將借出的對(duì)象釋放到對(duì)象池中。

class Program
{
    static async Task Main()
    {
        var objectPool = ObjectPool.Create<FoobarService>();
        while (true)
        {
            Console.Write("Used services: ");
            await Task.WhenAll(Enumerable.Range(1, 3).Select(_ => ExecuteAsync()));
            Console.Write("\n");
        }
        async Task ExecuteAsync()
        {
            var service = objectPool.Get();
            try
            {
                Console.Write($"{service.Id}; ");
                await Task.Delay(1000);
            }
            finally
            {
                objectPool.Return(service);
            }
        }
    }
}

在Main方法中,我們構(gòu)建了一個(gè)無(wú)限循環(huán),并在每次迭代中并行執(zhí)行ExecuteAsync方法三次。演示實(shí)例運(yùn)行之后會(huì)在控制臺(tái)上輸出如下所示的結(jié)果,可以看出每輪迭代使用的三個(gè)對(duì)象都是一樣的。每次迭代,它們從對(duì)象池中被借出,使用完之后又回到池中供下一次迭代使用。

二、依賴(lài)注入

我們知道依賴(lài)注入是已經(jīng)成為 .NET Core的基本編程模式,針對(duì)對(duì)象池的編程最好也采用這樣的編程方式。如果采用依賴(lài)注入,容器提供的并不是代表對(duì)象池的ObjectPool<T>對(duì)象,而是一個(gè)ObjectPoolProvider對(duì)象。顧名思義, ObjectPoolProvider對(duì)象作為對(duì)象池的提供者,用來(lái)提供針對(duì)指定對(duì)象類(lèi)型的ObjectPool<T>對(duì)象。

.NET提供的大部分框架都提供了針對(duì)IServiceCollection接口的擴(kuò)展方法來(lái)注冊(cè)相應(yīng)的服務(wù),但是對(duì)象池框架并沒(méi)有定義這樣的擴(kuò)展方法,所以我們需要采用原始的方式來(lái)完成針對(duì)ObjectPoolProvider的注冊(cè)。如下面的代碼片段所示,在創(chuàng)建出ServiceCollection對(duì)象之后,我們通過(guò)調(diào)用AddSingleton擴(kuò)展方法注冊(cè)了ObjectPoolProvider的默認(rèn)實(shí)現(xiàn)類(lèi)型DefaultObjectPoolProvider

class Program
{
    static async Task Main()
    {
        var objectPool = new ServiceCollection().AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
            .BuildServiceProvider()
            .GetRequiredService<ObjectPoolProvider>()
            .Create<FoobarService>();
        …
    }
}

在利用ServiceCollection對(duì)象創(chuàng)建出代表依賴(lài)注入容器的IServiceProvider對(duì)象之后,我們利用它提取出ObjectPoolProvider對(duì)象,并通過(guò)調(diào)用其Create<T>方法得到表示對(duì)象池的ObjectPool<FoobarService>對(duì)象。改動(dòng)的程序執(zhí)行之后同樣會(huì)在控制臺(tái)輸出如上圖所示的結(jié)果。

三、池化對(duì)象策略

通過(guò)前面的實(shí)例演示可以看出,對(duì)象池在默認(rèn)情況下會(huì)幫助我們完成對(duì)象的創(chuàng)建工作。我們可以想得到,它會(huì)在對(duì)象池?zé)o可用對(duì)象的時(shí)候會(huì)調(diào)用默認(rèn)的構(gòu)造函數(shù)來(lái)創(chuàng)建提供的對(duì)象。如果池化對(duì)象類(lèi)型沒(méi)有默認(rèn)的構(gòu)造函數(shù)呢?或者我們希望執(zhí)行一些初始化操作呢?

在另一方面,當(dāng)不在使用的對(duì)象被歸還到對(duì)象池之前,很有可能會(huì)執(zhí)行一些釋放性質(zhì)的操作(比如集合對(duì)象在歸還之前應(yīng)該被清空)。還有一種可能是對(duì)象有可能不能再次復(fù)用(比如它內(nèi)部維護(hù)了一個(gè)處于錯(cuò)誤狀態(tài)并無(wú)法恢復(fù)的網(wǎng)絡(luò)連接),那么它就不能被釋放會(huì)對(duì)象池。上述的這些需求都可以通過(guò)IPooledObjectPolicy<T>接口表示的池化對(duì)象策略來(lái)解決。

同樣以我們演示實(shí)例中使用的FoobarService類(lèi)型,如果并不希望用戶(hù)直接調(diào)用構(gòu)造函數(shù)來(lái)創(chuàng)建對(duì)應(yīng)的實(shí)例,所以我們按照如下的方式將其構(gòu)造函數(shù)改為私有,并定義了一個(gè)靜態(tài)的工廠(chǎng)方法Create來(lái)創(chuàng)建FoobarService對(duì)象。當(dāng)FoobarService類(lèi)型失去了默認(rèn)的無(wú)參構(gòu)造函數(shù)之后,我們演示的程序?qū)o(wú)法編譯。

public class FoobarService
{
    internal static int _latestId;
    public int Id { get; }
    private FoobarService() => Id = Interlocked.Increment(ref _latestId);
    public static FoobarService Create() => new FoobarService();
}

為了解決這個(gè)問(wèn)題,我們?yōu)镕oobarService類(lèi)型定義一個(gè)代表池化對(duì)象策略的FoobarPolicy類(lèi)型。如代碼片段所示,F(xiàn)oobarPolicy類(lèi)型實(shí)現(xiàn)了IPooledObjectPolicy<FoobarService>接口,實(shí)現(xiàn)的Create方法通過(guò)調(diào)用FoobarSerivice類(lèi)型的靜態(tài)同名方法完成針對(duì)對(duì)象的創(chuàng)建。另一個(gè)方法Return可以用來(lái)執(zhí)行一些對(duì)象歸還前的釋放操作,它的返回值表示該對(duì)象還能否回到池中供后續(xù)使用。由于FoobarService對(duì)象可以被無(wú)限次復(fù)用,所以實(shí)現(xiàn)的Return方法直接返回True。

public class FoobarPolicy : IPooledObjectPolicy<FoobarService>
{
    public FoobarService Create() => FoobarService.Create();
    public bool Return(FoobarService obj) => true;
}

在調(diào)用ObjectPoolProvider對(duì)象的Create<T>方法針對(duì)指定的類(lèi)型創(chuàng)建對(duì)應(yīng)的對(duì)象池的時(shí)候,我們將一個(gè)IPooledObjectPolicy<T>對(duì)象作為參數(shù),創(chuàng)建的對(duì)象池將會(huì)根據(jù)該對(duì)象定義的策略來(lái)創(chuàng)建和釋放對(duì)象。

class Program
{
    static async Task Main()
    {
        var objectPool = new ServiceCollection().AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
            .BuildServiceProvider()
            .GetRequiredService<ObjectPoolProvider>()
            .Create(new FoobarPolicy());
         …
     }
}

四、對(duì)象池的大小

對(duì)象池容納對(duì)象的數(shù)量總歸是有限的,默認(rèn)情況下它的大小為當(dāng)前機(jī)器處理器數(shù)量的2倍,這一點(diǎn)可以通過(guò)一個(gè)簡(jiǎn)單的實(shí)例來(lái)驗(yàn)證一下。如下面的代碼片段所示,我們將演示程序中每次迭代并發(fā)執(zhí)行ExecuteAsync方法的數(shù)量設(shè)置為當(dāng)前機(jī)器處理器數(shù)量的2倍,并將最后一次創(chuàng)建的FoobarService對(duì)象的ID打印出來(lái)。為了避免控制臺(tái)上的無(wú)效輸出,我們將ExecuteAsync方法中的控制臺(tái)輸出代碼移除。

class Program
{
    static async Task Main()
    {
        var objectPool = new ServiceCollection().AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
            .BuildServiceProvider()
            .GetRequiredService<ObjectPoolProvider>()
            .Create(new FoobarPolicy());
        var poolSize = Environment.ProcessorCount * 2;
        while (true)
        {
            while (true)
            {
                await Task.WhenAll(Enumerable.Range(1, poolSize).Select(_ => ExecuteAsync()));
                Console.WriteLine($"Last service: {FoobarService._latestId}");
            }
        }

        async Task ExecuteAsync()
        {
            var service = objectPool.Get();
            try
            {
                await Task.Delay(1000);
            }
            finally
            {
                objectPool.Return(service);
            }
        }
    }
}

上面這個(gè)演示實(shí)例表達(dá)的意思是:對(duì)象池的大小和對(duì)象消費(fèi)率剛好是一致的。在這種情況下,消費(fèi)的每一個(gè)對(duì)象都是從對(duì)象池中提取出來(lái),并且能夠成功還回去,那么對(duì)象的創(chuàng)建數(shù)量就是對(duì)象池的大小。下圖所示的是演示程序運(yùn)行之后再控制臺(tái)上的輸出結(jié)果,整個(gè)應(yīng)用的生命周期范圍內(nèi)一共只會(huì)有16個(gè)對(duì)象被創(chuàng)建出來(lái),因?yàn)槲耶?dāng)前機(jī)器的處理器數(shù)量為8。

如果對(duì)象池的大小為當(dāng)前機(jī)器處理器數(shù)量的2倍,那么我們倘若將對(duì)象的消費(fèi)率提高,意味著池化的對(duì)象將無(wú)法滿(mǎn)足消費(fèi)需求,新的對(duì)象將持續(xù)被創(chuàng)建出來(lái)。為了驗(yàn)證我們的想法,我們按照如下的方式將每次迭代執(zhí)行任務(wù)的數(shù)量加1。

class Program
{
    static async Task Main()
    {
        var objectPool = new ServiceCollection().AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
            .BuildServiceProvider()
            .GetRequiredService<ObjectPoolProvider>()
            .Create(new FoobarPolicy());
        var poolSize = Environment.ProcessorCount * 2;
        while (true)
        {
            while (true)
            {
                await Task.WhenAll(Enumerable.Range(1, poolSize + 1)
                    .Select(_ => ExecuteAsync()));
                Console.WriteLine($"Last service: {FoobarService._latestId}");
            }
        }
        …
    }
}

再次運(yùn)行改動(dòng)后的程序,我們會(huì)在控制臺(tái)上看到如下圖所示的輸出結(jié)果。由于每次迭代針對(duì)對(duì)象的需求量是17,但是對(duì)象池只能提供16個(gè)對(duì)象,所以每次迭代都必須額外創(chuàng)建一個(gè)新的對(duì)象。

五、對(duì)象的釋放

由于對(duì)象池容納的對(duì)象數(shù)量是有限的,如果現(xiàn)有的所有對(duì)象已經(jīng)被提取出來(lái),它會(huì)提供一個(gè)新創(chuàng)建的對(duì)象。從另一方面講,我們從對(duì)象池得到的對(duì)象在不需要的時(shí)候總是會(huì)還回去,但是對(duì)象池可能容不下那么多對(duì)象,它只能將其丟棄,被丟棄的對(duì)象將最終被GC回收。如果對(duì)象類(lèi)型實(shí)現(xiàn)了IDisposable接口,在它不能回到對(duì)象池的情況下,它的Dispose方法應(yīng)該被立即執(zhí)行。

為了驗(yàn)證不能正?;貧w對(duì)象池的對(duì)象能否被及時(shí)釋放,我們?cè)俅螌?duì)演示的程序作相應(yīng)的修改。我們讓FoobarService類(lèi)型實(shí)現(xiàn)IDisposable接口,并在實(shí)現(xiàn)的Dispose方法中將自身ID輸出到控制臺(tái)上。然后我們按照如下的方式以每次迭代并發(fā)量高于對(duì)象池大小的方式消費(fèi)對(duì)象。

class Program
{
    static async Task Main()
    {
        var objectPool = new ServiceCollection().AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
            .BuildServiceProvider()
            .GetRequiredService<ObjectPoolProvider>()
            .Create(new FoobarPolicy());

        while (true)
        {
            Console.Write("Disposed services:");
            await Task.WhenAll(Enumerable.Range(1, Environment.ProcessorCount * 2 + 3).Select(_ => ExecuteAsync()));
            Console.Write("\n");
        }

        async Task ExecuteAsync()
        {
            var service = objectPool.Get();
            try
            {
                await Task.Delay(1000);
            }
            finally
            {
                objectPool.Return(service);
            }
        }
    }
}

public class FoobarService: IDisposable
{
    internal static int _latestId;
    public int Id { get; }
    private FoobarService() => Id = Interlocked.Increment(ref _latestId);
    public static FoobarService Create() => new FoobarService();
    public void Dispose() => Console.Write($"{Id}; ");
}

演示程序運(yùn)行之后會(huì)在控制臺(tái)上輸出如下圖所示的結(jié)果,可以看出對(duì)于每次迭代消費(fèi)的19個(gè)對(duì)象,只有16個(gè)能夠正常回歸對(duì)象池,有三個(gè)將被丟棄并最終被GC回收。由于這樣的對(duì)象將不能被復(fù)用,它的Dispose方法會(huì)被調(diào)用,我們定義其中的釋放操作得以被及時(shí)執(zhí)行。

.NET Core對(duì)象池的應(yīng)用:設(shè)計(jì)篇
.NET Core對(duì)象池的應(yīng)用:擴(kuò)展篇

到此這篇關(guān)于.NET Core對(duì)象池的應(yīng)用:編程篇的文章就介紹到這了,更多相關(guān).NET Core對(duì)象池的應(yīng)用內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論