2009-04-02 3 views
0

나는 시험 적으로이 방법을 서면으로 작성했습니다 :이 캐싱 기능이 작동하는 방식으로 작동합니까?

public static Func<T> WeakCacheFor<T>(Func<T> provider) where T: class 
{ 
    var cache = new WeakReference(null); 
    return() => { 
     var x = (T)cache.Target; 
     if(x == null) 
     { 
      x = provider(); 
      cache.Target = x; 
     } 
     return x; 
    }; 
} 

그래서 약간의 배경 : 나는 조금 다음과 같이 몇 가지 장황한 기존의 방법이

:

var id = GetDatabaseId(); 
if(a){ 
    var data = GetLoader().Init(id).GetData(); // expensive! 
    // do stuff with data 
} 
if(b){ 
    // don't load data 
} 
... lots more variations, some contain GetLoader().Init(id).GetData(); some don't.... 

내 가능한 해결책은 다음과 같습니다.

var id = GetDatabaseId(); 
var loadData = WeakCacheFor(() => GetLoader().Init(id).GetData()); 
if(a){ 
    var data = loadData(); 
    // do stuff with data 
} 
if(b){ 
    // don't load data 
} 
... lots more variations, some contain loadData(); some don't.... 

내 생각 이 약의 :이 메서드 호출의 범위를 넘어 캐시 할 필요가 없습니다, 그래서 GC가 그것을 수집하는 경우 방법은

  • 를 반환으로 코드 아무튼 경로를 걸리는 경우 즉시 괜찮

    • 데이터를로드 할 필요가 없습니다. 히트는 발생하지 않습니다.
    • 데이터가 필요한 경우 다시 필요한 경우 약한 참조에 캐시됩니다.
    • GC가 중간 지점을 수집하는 경우 다시로드되기 때문에 중요하지 않습니다.

    내 질문 :

    1. 이 실제로 작동합니까? - WeakCacheFor 방법에서 놓친 부분이있어 강력하게 참조 할 수 없습니까?
    2. 나는 내 자신을 위해서 너무 똑똑한가요? - 필요하지 않은 경우에도 데이터를 히트하고 정상 로컬 변수에 캐시해야합니까?

    나는 너무 영리하다고 생각하지만, 비록 내가 그렇다고해도, 다른 상황에서 유용하게 적용될 수있는 해결책과 같은 다른 사람에게 보이나요 ??

    업데이트 : 수정 기능 때문에 apparently you can't trust .IsAlive

    업데이트 : 반환 된 Func이 방법의 끝에서 범위를 벗어난 갈 것이다, 그래서 모두에서 weakref를 필요로하지 않고 정상적인 심판이 작동 실현 잘 됐네. 나는 "나무를위한 숲을 볼 수 없다"고 생각했다.

  • 답변

    5

    약한 참조를 사용하는 데 어떤 점도 보이지 않습니다. 데이터를로드하고 나면 더 이상 유용하지 않을 때까지 버릴 이유가 거의 없습니다.

    구현중인 것은 지연로드 패턴의 변형입니다. 단순한 패턴 스틱 그냥 항목에 대한 일반 참조 사용

    public static Func<T> LazyLoad<T>(Func<T> provider) where T : class { 
        T item = null; 
        return() => { 
         if (item == null) { 
         item = provider(); 
         } 
         return item; 
        }; 
    } 
    

    (그리고 작은 팁 :. 당신은 너무 많이 var 키워드 방식을 사용하는)을 일반적인 의견의

    +0

    가 - 객체의 유형은 매우 분명하다 때 유용합니다 (예 : VAR NewList = List )하지만 많은 경우에는 그렇지 않습니다. –

    +0

    좋은 지적. 반환 된 람다 함수는 호출 메서드의 끝 부분에서 범위를 벗어나므로 weakref가 나에게 아무 것도 사지 않는다. var의 '남용'에 대해서는 동의하지 않습니다. var를 사용하지 않는 이유는 무엇입니까? –

    +0

    var 키워드를 사용하면 유형이 무엇인지, 어디서 코드를 읽기 쉽도록하는지 분명하게 알 수 있습니다. 오른쪽면이 유형을 표시하지 않으면 유형 이름을 사용해야합니다 (예 : 예에서 ID 및 데이터 변수). – Guffa

    2

    커플 :

    1. 귀하의 솔루션 (뿐만 아니라)은 스레드로부터 안전하지 않습니다.
    2. IDisposable 개체와 함께 작동하도록 설계되지 않았습니다.

    다음 설명은 고려 :

    foo = WeakCacheFor<Foo>(() => CreateFoo()); 
    

    사례 # 1 : 긴 살아있는 변수로 foo를 사용 (예 : 긴 살아있는 클래스의 멤버, 또는 글로벌 변수)

    귀하 해결책은 여기에 있습니다. 변수는 필요할 때 생성되며 시스템이 GC 동안 자원을 비울 때 파괴됩니다.

    하지만 foo가 시간이 많이 들지만 메모리가 저렴하다면 아마도 싱글 톤 패턴을 대신 사용하고 응용 프로그램을 실행하는 동안 한 번로드하는 것이 좋습니다.

    사례 # 2. foo를 지역 변수로 사용.

    이 경우 싱글 톤 패턴을 사용하는 것이 좋습니다. 다음 예제를 고려하십시오.

    static void Main(string[] args) 
    { 
        MethodA(5, 7); 
        MethodA(8, 9); 
    } 
    
    static void MethodA(int a, int b) 
    { 
        var foo = WeakCacheFor<Foo>(() => new Foo()); 
    
        if (a > 3) 
        { 
         Use(foo); 
    
         if (a * b == 35) 
         { 
          GC.Collect(); // Simulate GC 
          Use(foo); 
         } 
         else if(b % 6 == 2) 
         { 
          Use(foo); 
         } 
        } 
    } 
    

    foo는 3 번 생성됩니다. 그리고 "Simulate GC"라인에 의견을 말하면 2 번. 또한 IDisposable 클래스에 이것을 사용할 수 없습니다. 당신이 볼 수 있듯이

    static void MethodA(int a, int b) 
    { 
        using (var foo = new MySingleton<Foo>(() => new Foo())) 
        { 
         if (a > 3) 
         { 
          Use(foo); 
    
          if (a * b == 35) 
          { 
           GC.Collect(); // Simulate GC 
           Use(foo); 
          } 
          else if (b % 6 == 2) 
          { 
           Use(foo); 
          } 
         } 
        } 
    } 
    

    , 코드가 거의 변화하지 않았다, 그러나 지금 우리는 푸의 ctor와는 IDisposable 지원 만이 전화를받을 :

    는 이제 싱글을 해보자.

    거친 싱글 구현 :

    class MySingleton<T> : IDisposable 
        where T : class 
    { 
        private T _value; 
        private Func<T> _provider; 
    
        public MySingleton(Func<T> provider) 
        { 
         _provider = provider; 
        } 
    
        public T Get() 
        { 
         if (_value == null) 
         { 
          _value = _provider(); 
         } 
    
         return _value; 
        } 
    
        #region IDisposable Members 
    
        public void Dispose() 
        { 
         if(_value == null) 
          return; 
    
         IDisposable disposable = _value as IDisposable; 
    
         if(disposable != null) 
          disposable.Dispose(); 
        } 
    
        #endregion 
    } 
    

    그리고 나머지 코드 :

    내가 var 키워드에 동의
    class Foo : IDisposable 
    { 
        public void Dispose() {} 
    } 
    
    static void Use(MySingleton<Foo> foo) 
    { 
        foo.Get(); 
    } 
    
    static void Use(Func<Foo> foo) 
    { 
        foo(); 
    } 
    
    +0

    나는 싱글 톤 팬이 아니지만 당신의 통찰력에 감사드립니다! +1 –

    +0

    Btw, 아마도 'Singleton'은이 구현의 가장 좋은 이름이 아니며 '지연로드'가 더 좋을 수 있습니다. – alex2k8

    관련 문제