5

次のような Windows Server AppFabric SDK にある Microsoft.ApplicationServer.Caching.DataCache オブジェクトの拡張メソッドがあります。

using System;
using System.Collections.Generic;
using Microsoft.ApplicationServer.Caching;

namespace Caching
{
    public static class CacheExtensions
    {
        private static Dictionary<string, object> locks = new Dictionary<string, object>();

        public static T Fetch<T>(this DataCache @this, string key, Func<T> func)
        {
            return @this.Fetch(key, func, TimeSpan.FromSeconds(30));
        }

        public static T Fetch<T>(this DataCache @this, string key, Func<T> func, TimeSpan timeout)
        {
            var result = @this.Get(key);

            if (result == null)
            {
                lock (GetLock(key))
                {
                    result = @this.Get(key);

                    if (result == null)
                    {
                        result = func();

                        if (result != null)
                        {
                            @this.Put(key, result, timeout);
                        }
                    }
                }
            }

            return (T)result;
        }

        private static object GetLock(string key)
        {
            object @lock = null;

            if (!locks.TryGetValue(key, out @lock))
            {
                lock (locks)
                {
                    if (!locks.TryGetValue(key, out @lock))
                    {
                        @lock = new object();
                        locks.Add(key, @lock);
                    }
                }
            }

            return @lock;
        }
    }
}

その意図は、開発者が「最初にキャッシュを試してデータをフェッチしてください。キャッシュで使用できない場合は、指定された関数を実行し、次の呼び出し元のために結果をキャッシュに入れ、結果を返す」というコードを記述できるようにすることです。このような:

var data = dataCache.Fetch("key", () => SomeLongRunningOperation());

ロックは、長時間実行される可能性のある関数呼び出しの実行を 1 つのスレッドに制限しますが、同じマシン上の 1 つのプロセス内に限定されます。複数のプロセス/マシンが一度に機能を実行するのを防ぐために、このパターンをどのように拡張してロックを分散させますか?

4

2 に答える 2

8

GetAndLock/PutAndUnlockAppFabric には、一連のメソッドを通じてアクセスできる独自の分散ロック メカニズムがあります。アイテムがロックされている場合、通常のGet呼び出しは引き続き成功し、最後の値を返しますが、それ以上のGetAndLock呼び出しは例外をスローします。クライアントがキャッシュされたオブジェクトを初めて要求する場合、キーがまだ実際には存在していなくても、キーをロックできます (ソリッド ロックというよりは予約のようなものです)。

public static T Fetch<T>(this DataCache @this, string key, Func<T> func, TimeSpan timeout)
{
    var result = @this.Get(key);

    if (result == null)
    (
        DataCacheLockHandle handle;
        // We need a timespan to allow func time to run
        TimeSpan funcTimespan = New TimeSpan(0,1,0);

        try
        {
            // Lock the key
            // If something goes wrong here it will unlock at the end of funcTimespan
            var result = @this.GetAndLock(key, funcTimespan, handle);

            if (result == null)
            {
                // Still no value so go and run func
                result = func();

                @this.PutAndUnlock(key, result, handle, timeout);
            }
            else
            {
                // There's a value now so we'll unlock the key and reset it's timeout
                @this.Unlock(key, handle, timeout);
            }
        }
        catch (DataCacheException ex)
        {
            if (ex.ErrorCode == DataCacheErrorCode.ObjectLocked)
            {
                // Another process has locked the key so func must be running right now
                // We'll return null to the client
                result = null;
            }
        }

        if (result == null)
        {
            return null;
        }
        else
        {
            return (T)result;
        }
    )
}
于 2011-02-24T12:58:30.803 に答える