最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

C# .NET 中的緩存實(shí)現(xiàn)詳情

 更新時(shí)間:2021年09月13日 10:43:42   作者:michaelscoding  
軟件開(kāi)發(fā)中最常用的模式之一是 緩存 ,其包括進(jìn)程內(nèi)緩存、持久性進(jìn)程內(nèi)緩存和分布式緩存,本文我們將主要介紹進(jìn)程內(nèi)緩存,需要的朋友可以參考下面文章的具體內(nèi)容

一、緩存的基本概念

緩存 。這是一個(gè)簡(jiǎn)單但非常有效的概念,這個(gè)想法的核心是記錄過(guò)程數(shù)據(jù),重用操作結(jié)果。當(dāng)執(zhí)行繁重的操作時(shí),我們會(huì)將結(jié)果保存在我們的 緩存容器中 。下次我們需要該結(jié)果時(shí),我們將從緩存容器中拉出它,而不是再次執(zhí)行繁重的操作。

例如,要獲取一個(gè)人的頭像,您可能需要訪問(wèn)數(shù)據(jù)庫(kù)。我們不會(huì)每次都執(zhí)行那次旅行,而是將 Avatar 保存在緩存中,每次需要時(shí)從內(nèi)存中提取它。

緩存非常適用于不經(jīng)常更改的數(shù)據(jù)。或者甚至更好,永遠(yuǎn)不會(huì)改變。不斷變化的數(shù)據(jù),比如當(dāng)前機(jī)器的時(shí)間不應(yīng)該被緩存,否則你會(huì)得到錯(cuò)誤的結(jié)果。

二、緩存

有 3 種類(lèi)型的緩存:

  • In-Memory Cache: 用于在單個(gè)進(jìn)程中實(shí)現(xiàn)緩存。當(dāng)進(jìn)程終止時(shí),緩存也隨之終止。如果您在多臺(tái)服務(wù)器上運(yùn)行相同的進(jìn)程,您將為每臺(tái)服務(wù)器提供一個(gè)單獨(dú)的緩存。
  •  持久性進(jìn)程內(nèi)緩存: 是指在進(jìn)程內(nèi)存之外備份緩存。它可能在文件中,也可能在數(shù)據(jù)庫(kù)中。這比較困難,但如果您的進(jìn)程重新啟動(dòng),緩存不會(huì)丟失。最適合在獲取緩存項(xiàng)的情況下使用范圍廣泛,并且您的進(jìn)程往往會(huì)重新啟動(dòng)很多。
  • 分布式緩存: 是指您希望為多臺(tái)機(jī)器共享緩存。通常,它將是多個(gè)服務(wù)器。使用分布式緩存,它存儲(chǔ)在外部服務(wù)中。這意味著如果一臺(tái)服務(wù)器保存了一個(gè)緩存項(xiàng),其他服務(wù)器也可以使用它。像 Redis [1] 這樣的服務(wù)非常適合這一點(diǎn)。

我們將只討論 進(jìn)程內(nèi)緩存 。

三、進(jìn)程內(nèi)緩存早期做法

讓我們用 C# 創(chuàng)建一個(gè)非常簡(jiǎn)單的緩存實(shí)現(xiàn):

public class NaiveCache<TItem>


{


    Dictionary<object, TItem> _cache = new Dictionary<object, TItem>();

 

 

    public TItem GetOrCreate(object key, Func<TItem> createItem)


    {


        if (!_cache.ContainsKey(key))


        {


            _cache[key] = createItem();


        }


        return _cache[key];


    }


}

用法:

var _avatarCache = new NaiveCache<byte[]>();


// ...


var myAvatar = _avatarCache.GetOrCreate(userId, () => _database.GetAvatar(userId));

這個(gè)簡(jiǎn)單的代碼解決了一個(gè)關(guān)鍵問(wèn)題。要獲取用戶(hù)的頭像,只有第一個(gè)請(qǐng)求才會(huì)真正執(zhí)行到數(shù)據(jù)庫(kù)的訪問(wèn)。然后將頭像數(shù)據(jù) ( byte[] ) 保存在進(jìn)程內(nèi)存中。對(duì)頭像的所有后續(xù)請(qǐng)求都將從內(nèi)存中提取,從而節(jié)省時(shí)間和資源。

但是,正如編程中的大多數(shù)事情一樣,沒(méi)有什么是那么簡(jiǎn)單的。由于多種原因,上述解決方案并不好。一方面,這個(gè)實(shí)現(xiàn) 不是線程安全的 。從多個(gè)線程使用時(shí)可能會(huì)發(fā)生異常。除此之外,緩存的項(xiàng)目將永遠(yuǎn)留在內(nèi)存中,這實(shí)際上非常糟糕。

這就是我們應(yīng)該從緩存中刪除項(xiàng)目的原因:

  • 緩存會(huì)占用大量?jī)?nèi)存,最終導(dǎo)致內(nèi)存不足異常和崩潰。
  • 高內(nèi)存消耗會(huì)導(dǎo)致 GC 壓力 (又名內(nèi)存壓力)。在這種狀態(tài)下,垃圾收集器的工作量超出其應(yīng)有的水平,從而損害了性能。
  •  如果數(shù)據(jù)發(fā)生變化,可能需要刷新緩存。我們的緩存基礎(chǔ)設(shè)施應(yīng)該支持這種能力。

為了處理這些問(wèn)題,緩存框架具有 驅(qū)逐策略 (又名 移除策略 )。這些是根據(jù)某些邏輯從緩存中刪除項(xiàng)目的規(guī)則。常見(jiàn)的驅(qū)逐政策有:

  • 無(wú)論如何, 絕對(duì)過(guò)期 策略將在固定時(shí)間后從緩存中刪除項(xiàng)目。
  • 如果在固定的時(shí)間段內(nèi)未 訪問(wèn) 某個(gè)項(xiàng)目,則 滑動(dòng)過(guò)期 策略將從緩存中刪除該項(xiàng)目。因此,如果我將過(guò)期時(shí)間設(shè)置為 1 分鐘,只要我每 30 秒使用一次,該項(xiàng)目就會(huì)一直保留在緩存中。一旦我超過(guò)一分鐘不使用它,該物品就會(huì)被驅(qū)逐。
  • 大小限制 策略將限制緩存內(nèi)存大小。

現(xiàn)在我們知道我們需要什么,讓我們繼續(xù)尋找更好的解決方案。

四、更好的解決方案

作為一名博主,令我非常沮喪的是,微軟已經(jīng)創(chuàng)建了一個(gè)很棒的緩存實(shí)現(xiàn)。這剝奪了我自己創(chuàng)建類(lèi)似實(shí)現(xiàn)的樂(lè)趣,但至少我寫(xiě)這篇博文的工作量減少了。

我將向您展示微軟的解決方案,如何有效地使用它,然后在某些場(chǎng)景中如何改進(jìn)它。

System.Runtime.Caching/MemoryCache 與 Microsoft.Extensions.Caching.Memory

Microsoft 有 2 個(gè)解決方案 2 個(gè)不同的 NuGet 包用于緩存。兩者都很棒。根據(jù) Microsoft 的 建議 ,更喜歡使用, Microsoft.Extensions.Caching.Memory 因?yàn)樗c Asp.NET Core 集成得更好。它可以很 容易地注入 到 Asp .NET Core 的依賴(lài)注入機(jī)制中。

1、 Microsoft.Extensions.Caching.Memory

這是一個(gè)基本示例 Microsoft.Extensions.Caching.Memory :

public class SimpleMemoryCache<TItem>


{

    private MemoryCache _cache = new MemoryCache(new MemoryCacheOptions()); 

    public TItem GetOrCreate(object key, Func<TItem> createItem)


    {


        TItem cacheEntry;


        if (!_cache.TryGetValue(key, out cacheEntry))// Look for cache key.


        {


            // Key not in cache, so get data.


            cacheEntry = createItem();


            // Save data in cache.


            _cache.Set(key, cacheEntry);


        }


        return cacheEntry;
    }
}

用法:

var _avatarCache = new SimpleMemoryCache<byte[]>();


// ...


var myAvatar = _avatarCache.GetOrCreate(userId, () => _database.GetAvatar(userId));

這和我自己的非常相似 NaiveCache ,所以有什么改變?嗯,一方面,這是一個(gè) 線程安全的 實(shí)現(xiàn)。您可以一次從多個(gè)線程安全地調(diào)用它。

第二件事是 MemoryCache 允許我們之前談到的所有 驅(qū)逐政策 。

下面是一個(gè)例子:

2、具有驅(qū)逐策略的 IMemoryCache

public class MemoryCacheWithPolicy<TItem>
{
    private MemoryCache _cache = new MemoryCache(new MemoryCacheOptions()

    {
        SizeLimit = 1024

    });

    public TItem GetOrCreate(object key, Func<TItem> createItem)

  {
        TItem cacheEntry;
        if (!_cache.TryGetValue(key, out cacheEntry))// Look for cache key.

        {
            // Key not in cache, so get data.
            cacheEntry = createItem();

            var cacheEntryOptions = new MemoryCacheEntryOptions()
             .SetSize(1)//Size amount
             //Priority on removing when reaching size limit (memory pressure)

                .SetPriority(CacheItemPriority.High)

            // Keep in cache for this time, reset time if accessed.

             .SetSlidingExpiration(TimeSpan.FromSeconds(2))

              // Remove from cache after this time, regardless of sliding expiration
                .SetAbsoluteExpiration(TimeSpan.FromSeconds(10));

            // Save data in cache.
            _cache.Set(key, cacheEntry, cacheEntryOptions);
        }
        return cacheEntry;
    }
}
  • SizeLimit 被添加到 MemoryCacheOptions . 這為我們的緩存容器添加了基于大小的策略。大小沒(méi)有單位。相反,我們需要在每個(gè)緩存條目上設(shè)置大小數(shù)量。在這種情況下,我們每次將金額設(shè)置為 1 SetSize(1) 。這意味著緩存限制為 1024 個(gè)項(xiàng)目。
  •   當(dāng)我們達(dá)到大小限制時(shí),應(yīng)該刪除哪個(gè)緩存項(xiàng)?您實(shí)際上可以使用 .SetPriority(CacheItemPriority.High) . 級(jí)別為 Low、Normal、High 和 NeverRemove
  • SetSlidingExpiration(TimeSpan.FromSeconds(2)) 添加了,它將 滑動(dòng)過(guò)期 時(shí)間設(shè)置為 2 秒。這意味著如果一個(gè)項(xiàng)目在 2 秒內(nèi)未被訪問(wèn),它將被刪除。
  •   SetAbsoluteExpiration(TimeSpan.FromSeconds(10)) 添加了,將 絕對(duì)過(guò)期 時(shí)間設(shè)置為 10 秒。這意味著該項(xiàng)目將在 10 秒內(nèi)被驅(qū)逐,如果它還沒(méi)有。
  • 除了示例中的選項(xiàng)之外,您還可以設(shè)置一個(gè) RegisterPostEvictionCallback 委托,該委托將在項(xiàng)目被驅(qū)逐時(shí)調(diào)用。
  • 這是一個(gè)非常全面的功能集。它讓你想知道是否還有什么要添加的。實(shí)際上有幾件事。

3、問(wèn)題和缺失的功能

在這個(gè)實(shí)現(xiàn)中有幾個(gè)重要的缺失部分。

  • 雖然您可以設(shè)置大小限制,但緩存實(shí)際上并不監(jiān)控 gc 壓力。如果真的監(jiān)測(cè),壓力大的時(shí)候可以收緊政策,壓力小的時(shí)候可以放松政策。
  • 當(dāng)多個(gè)線程同時(shí)請(qǐng)求同一個(gè)項(xiàng)目時(shí),請(qǐng)求不會(huì)等待第一個(gè)完成。該項(xiàng)目將被創(chuàng)建多次。例如,假設(shè)我們正在緩存頭像,從數(shù)據(jù)庫(kù)中獲取頭像需要 10 秒。如果我們?cè)诘谝淮握?qǐng)求后 2 秒請(qǐng)求頭像,它將檢查頭像是否已緩存(尚未緩存),并開(kāi)始另一次訪問(wèn)數(shù)據(jù)庫(kù)。

事實(shí)上,這是一個(gè) MemoryCache 完全解決它的實(shí)現(xiàn):

public class WaitToFinishMemoryCache<TItem>

{

    private MemoryCache _cache = new MemoryCache(new MemoryCacheOptions());
    private ConcurrentDictionary<object, SemaphoreSlim> _locks = new ConcurrentDictionary<object, SemaphoreSlim>();

    public async Task<TItem> GetOrCreate(object key, Func<Task<TItem>> createItem)

    {

        TItem cacheEntry;

        if (!_cache.TryGetValue(key, out cacheEntry))// Look for cache key.
        {
            SemaphoreSlim mylock = _locks.GetOrAdd(key, k => new SemaphoreSlim(1, 1));

            await mylock.WaitAsync();
            try
            {
                if (!_cache.TryGetValue(key, out cacheEntry))
                {
                    // Key not in cache, so get data.
                    cacheEntry = await createItem();

                    _cache.Set(key, cacheEntry);
                }
            }
            finally
            {
                mylock.Release();
            }

        }

        return cacheEntry;
    }

}

用法:

var _avatarCache = new WaitToFinishMemoryCache<byte[]>();
// ...
var myAvatar = 

 await _avatarCache.GetOrCreate(userId, async () => await _database.GetAvatar(userId));

4、代碼說(shuō)明

此實(shí)現(xiàn)鎖定項(xiàng)目的創(chuàng)建。鎖是特定于鑰匙的。例如,如果我們正在等待獲取 Alex Avatar,我們?nèi)匀豢梢栽诹硪粋€(gè)線程上獲取 John Sarah 的緩存值。

字典 _locks 存儲(chǔ)了所有的鎖。常規(guī)鎖不適用于 async/await ,因此我們需要使用 SemaphoreSlim [5] .

如果 (!_cache.TryGetValue(key, out cacheEntry)),有 2 次檢查以查看該值是否已被緩存。鎖內(nèi)的那個(gè)是確保只有一個(gè)創(chuàng)建的那個(gè)。鎖外面的那個(gè)是為了優(yōu)化。

五、何時(shí)使用 WaitToFinishMemoryCache

這個(gè)實(shí)現(xiàn)顯然有一些開(kāi)銷(xiāo)。讓我們考慮什么時(shí)候甚至有必要。

在以下情況下使用 WaitToFinishMemoryCache:

  • 當(dāng)項(xiàng)目的創(chuàng)建時(shí)間具有某種成本時(shí),您希望盡可能減少創(chuàng)建。
  • 當(dāng)一個(gè)項(xiàng)目的創(chuàng)建時(shí)間很長(zhǎng)時(shí)。
  • 當(dāng)必須確保每個(gè)鍵都創(chuàng)建一個(gè)項(xiàng)目時(shí)。

在以下情況下不要使用 WaitToFinishMemoryCache:

  • 沒(méi)有多個(gè)線程訪問(wèn)同一個(gè)緩存項(xiàng)的危險(xiǎn)。
  • 您不介意多次創(chuàng)建該項(xiàng)目。例如,如果對(duì)數(shù)據(jù)庫(kù)的額外訪問(wèn)不會(huì)有太大變化。

概括:

緩存是一種非常強(qiáng)大的模式,它也很危險(xiǎn),并且有其自身的復(fù)雜性。緩存太多,可能會(huì)導(dǎo)致 GC 壓力,緩存太少會(huì)導(dǎo)致性能問(wèn)題。而分布式緩存,這是一個(gè)需要探索的全新世界。軟件開(kāi)發(fā)職業(yè)就這樣,總是有新的東西要學(xué)習(xí)。

到此這篇關(guān)于C# .NET 中的緩存實(shí)現(xiàn)詳情的文章就介紹到這了,更多相關(guān)C# .NET 中的緩存實(shí)現(xiàn)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • C#使用ping命令的兩個(gè)例子

    C#使用ping命令的兩個(gè)例子

    這篇文章主要介紹了C#使用ping命令的兩個(gè)例子,本文給出了調(diào)用CMD命令和使用C# ping類(lèi)的兩個(gè)例子,需要的朋友可以參考下
    2014-08-08
  • .net使用Aspose.Words進(jìn)行Word替換操作的實(shí)現(xiàn)代碼

    .net使用Aspose.Words進(jìn)行Word替換操作的實(shí)現(xiàn)代碼

    之前在工作中,需要實(shí)現(xiàn)Word打印功能,并且插入圖片。當(dāng)時(shí)采取的方式則是使用書(shū)簽進(jìn)行操作。首先在word內(nèi)插入書(shū)簽,完成后,存為模板。程序加載該模板,找到書(shū)簽,并在指定位置寫(xiě)入文字即可
    2013-05-05
  • unity 如何使用文件流讀取streamingassets下的資源

    unity 如何使用文件流讀取streamingassets下的資源

    這篇文章主要介紹了unity 使用文件流讀取streamingassets下的資源操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2021-04-04
  • C# .Net8 switch的用法小結(jié)

    C# .Net8 switch的用法小結(jié)

    在 .net 8中,switch 不需要再和傳統(tǒng)的寫(xiě)法一樣了,會(huì)更加的方便,本文主要介紹了C# .Net8 switch的用法小結(jié),具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-05-05
  • C#中使用CAS實(shí)現(xiàn)無(wú)鎖算法的示例詳解

    C#中使用CAS實(shí)現(xiàn)無(wú)鎖算法的示例詳解

    CAS(Compare-and-Swap)是一種多線程并發(fā)編程中常用的原子操作,用于實(shí)現(xiàn)多線程間的同步和互斥訪問(wèn)。本文將利用CAS實(shí)現(xiàn)無(wú)鎖算法,需要的可以參考一下
    2023-04-04
  • C#利用RabbitMQ實(shí)現(xiàn)點(diǎn)對(duì)點(diǎn)消息傳輸

    C#利用RabbitMQ實(shí)現(xiàn)點(diǎn)對(duì)點(diǎn)消息傳輸

    RabbitMQ做為消息代理,負(fù)責(zé)接收和轉(zhuǎn)發(fā)消息,可以將RabbitMQ比喻為一個(gè)郵筒、一個(gè)郵局和一個(gè)郵遞員。本文主要以一個(gè)簡(jiǎn)單的小例子,簡(jiǎn)述RabbitMQ實(shí)現(xiàn)消息傳輸?shù)南嚓P(guān)內(nèi)容,僅供學(xué)習(xí)分享使用,如有不足之處,還請(qǐng)指正。
    2021-05-05
  • C#使用for循環(huán)移除HTML標(biāo)記

    C#使用for循環(huán)移除HTML標(biāo)記

    大家在項(xiàng)目開(kāi)發(fā)階段移除文字中的html標(biāo)記最常用的方法就是使用正則表達(dá)式,但是正則表達(dá)式不能處理所有的html文檔,所以采用迭代方式會(huì)更好,下面小編給大家解答下
    2016-08-08
  • C# ComboBox下拉框?qū)崿F(xiàn)搜索方式

    C# ComboBox下拉框?qū)崿F(xiàn)搜索方式

    文章介紹了如何在加載窗口時(shí)實(shí)現(xiàn)一個(gè)功能,并在ComboBox下拉框中添加鍵盤(pán)事件以實(shí)現(xiàn)搜索功能,由于數(shù)據(jù)不方便公開(kāi),作者表示理解并希望得到大家的指教
    2024-12-12
  • 利用C#代碼將html樣式文件與Word文檔互換的方法

    利用C#代碼將html樣式文件與Word文檔互換的方法

    這篇文章主要給大家介紹了關(guān)于利用C#代碼將html樣式文件與Word文檔互換的方法,文中通過(guò)示例代碼將兩種轉(zhuǎn)換介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。
    2017-11-11
  • c#使用微信接口開(kāi)發(fā)微信門(mén)戶(hù)應(yīng)用

    c#使用微信接口開(kāi)發(fā)微信門(mén)戶(hù)應(yīng)用

    本系列文章希望從一個(gè)循序漸進(jìn)的角度上,全面介紹微信的相關(guān)開(kāi)發(fā)過(guò)程和相關(guān)經(jīng)驗(yàn)總結(jié),希望給大家了解一下相關(guān)的開(kāi)發(fā)歷程。
    2014-03-03

最新評(píng)論

久治县| 始兴县| 鹤庆县| 新龙县| 松阳县| 绥江县| 枣庄市| 新宾| 莱州市| 会泽县| 锦州市| 嘉定区| 新乡市| 丽江市| 伊川县| 昆明市| 兰州市| 和静县| 定兴县| 深圳市| 德庆县| 柳江县| 长垣县| 寻甸| 万荣县| 阿克陶县| 深圳市| 丰县| 黑河市| 双城市| 拜泉县| 临澧县| 耒阳市| 宁国市| 克东县| 修文县| 德州市| 静安区| 乌拉特前旗| 蒙自县| 北辰区|