41

.net 4.5を使用してさまざまなアイテムを追跡し、自動的に更新しようとしていますがMemoryCache、何を設定しても、AbsoluteExpiration常に15秒以上で期限切れになるようです。

キャッシュアイテムを5秒ごとに期限切れにしたいのですが、常に少なくとも15秒で期限切れになります。有効期限をタイムアウトにすると、15秒+更新間隔のようになりますが、15秒以上になることはありません。 。

表示されていない内部タイマーの解像度はありますか?反映されたコードを少し調べたところ、System.Runtime.Caching.MemoryCache目立ったものは何もありませんでした。インターネット上でこの問題を抱えている人を見つけることはできませんでした。

問題を説明する非常に基本的な例を以下に示します。

私が望んでいるのは、CacheEntryUpdate5秒ごとにヒットし、新しいデータで更新することですが、私が言ったように、15秒以上でヒットするだけです。

static MemoryCache MemCache;
static int RefreshInterval = 5000;

protected void Page_Load(object sender, EventArgs e)
{
    if (MemCache == null)
        MemCache = new MemoryCache("MemCache");

    if (!MemCache.Contains("cacheItem"))
    {
        var cacheObj = new object();
        var policy = new CacheItemPolicy
        {
            UpdateCallback = new CacheEntryUpdateCallback(CacheEntryUpdate),
            AbsoluteExpiration = DateTimeOffset.UtcNow.AddMilliseconds(RefreshInterval)
        };
        var cacheItem = new CacheItem("cacheItem", cacheObj);
        MemCache.Set("cacheItem", cacheItem, policy);
    }
}

private void CacheEntryUpdate(CacheEntryUpdateArguments args)
{
    var cacheItem = MemCache.GetCacheItem(args.Key);
    var cacheObj = cacheItem.Value;

    cacheItem.Value = cacheObj;
    args.UpdatedCacheItem = cacheItem;
    var policy = new CacheItemPolicy
    {
        UpdateCallback = new CacheEntryUpdateCallback(CacheEntryUpdate),
        AbsoluteExpiration = DateTimeOffset.UtcNow.AddMilliseconds(RefreshInterval)
    };
    args.UpdatedCacheItemPolicy = policy;
}
4

4 に答える 4

48

私はそれを理解しました。internal static readonly TimeSpanSystem.Runtime.Caching.CacheExpiresに_tsPerBucketという名前があり、20秒でハードコードされています。

どうやら、このフィールドは、実行され、キャッシュアイテムの有効期限が切れているかどうかを確認する内部タイマーで使用されるものです。

リフレクションを使用して値を上書きし、デフォルトのMemoryCacheインスタンスをクリアしてすべてをリセットすることで、これを回避しています。巨大なハックであっても、うまくいくようです。

更新されたコードは次のとおりです。

static MemoryCache MemCache;
static int RefreshInterval = 1000;

protected void Page_Load(object sender, EventArgs e)
{
    if (MemCache == null)
    {
        const string assembly = "System.Runtime.Caching, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a";
        var type = Type.GetType("System.Runtime.Caching.CacheExpires, " + assembly, true, true);
        var field = type.GetField("_tsPerBucket", BindingFlags.Static | BindingFlags.NonPublic);
        field.SetValue(null, TimeSpan.FromSeconds(1));

        type = typeof(MemoryCache);
        field = type.GetField("s_defaultCache", BindingFlags.Static | BindingFlags.NonPublic);
        field.SetValue(null, null);

        MemCache = new MemoryCache("MemCache");
    }

    if (!MemCache.Contains("cacheItem"))
    {
        var cacheObj = new object();
        var policy = new CacheItemPolicy
        {
            UpdateCallback = new CacheEntryUpdateCallback(CacheEntryUpdate),
            AbsoluteExpiration = DateTimeOffset.UtcNow.AddMilliseconds(RefreshInterval)
        };
        var cacheItem = new CacheItem("cacheItem", cacheObj);
        MemCache.Set("cacheItem", cacheItem, policy);
    }
}

private void CacheEntryUpdate(CacheEntryUpdateArguments args)
{
    var cacheItem = MemCache.GetCacheItem(args.Key);
    var cacheObj = cacheItem.Value;

    cacheItem.Value = cacheObj;
    args.UpdatedCacheItem = cacheItem;
    var policy = new CacheItemPolicy
    {
        UpdateCallback = new CacheEntryUpdateCallback(CacheEntryUpdate),
        AbsoluteExpiration = DateTimeOffset.UtcNow.AddMilliseconds(RefreshInterval)
    };
    args.UpdatedCacheItemPolicy = policy;
}
于 2012-09-28T18:27:08.397 に答える
9

古いSystem.Runtime.Cachingから新しいMicrosft.Extensions.Cachingに変更してもよろしいですか?バージョン1.xは、netstandard1.3およびnet451をサポートします。もしそうなら、改善されたAPIは、リフレクションを伴うハッカーなしで、あなたが説明する使用法をサポートします。

MemoryCacheOptionsオブジェクトには、キャッシュクリーンアップのスキャン頻度を制御できるExpirationScanFrequencyプロパティがあります。https://docs.microsoft.com/en-us/dotnet/api/microsoft.extensions.caching.memory.memorycacheoptions.expirationscanfrequencyを参照してください。 ?view = aspnetcore-2.0

タイマーに基づく有効期限がなくなったことに注意してください(これはパフォーマンス設計の決定です)。そのため、メモリ不足またはキャッシュされたアイテムのGet()ベースのメソッドの1つを呼び出すことが、有効期限のトリガーになります。ただし、キャンセルトークンを使用して時間ベースの有効期限を強制することができます。例については、このSOの回答を参照してくださいhttps://stackoverflow.com/a/47949111/3140853

于 2018-02-27T11:26:03.883 に答える
4

MatteoSpへ-構成のpollingIntervalまたはコンストラクターのNameValueCollectionは別のタイマーです。これは、呼び出されたときに他の2つの構成プロパティを使用して、Trimメソッドを使用してエントリを削除する必要があるレベルにメモリがあるかどうかを判断する間隔です。

于 2014-02-17T19:19:05.077 に答える
1

@Jaredの回答に基づいた更新バージョン。デフォルトのMemoryCacheインスタンスを変更することで、ここで新しいインスタンスを作成します。

class FastExpiringCache
{
    public static MemoryCache Default { get; } = Create();

    private static MemoryCache Create()
    {
        MemoryCache instance = null;
        Assembly assembly = typeof(CacheItemPolicy).Assembly;
        Type type = assembly.GetType("System.Runtime.Caching.CacheExpires");
        if( type != null)
        {
            FieldInfo field = type.GetField("_tsPerBucket", BindingFlags.Static | BindingFlags.NonPublic);
            if(field != null && field.FieldType == typeof(TimeSpan))
            {
                TimeSpan originalValue = (TimeSpan)field.GetValue(null);
                field.SetValue(null, TimeSpan.FromSeconds(3));
                instance = new MemoryCache("FastExpiringCache");
                field.SetValue(null, originalValue); // reset to original value
            }
        }
        return instance ?? new MemoryCache("FastExpiringCache");
    }
}
于 2017-04-25T08:20:55.373 に答える