2010-08-12 4 views
2

나는 데이터베이스에서 가져온 것을 기반으로 물건을 계산하기 위해 상당한 양의 요청에 사용하는 수천 개의 정수 (lookup table) (LUT)를 가지고있다.싱글 톤은 ASP.NET MVC의 요청간에 자동으로 유지됩니까?

LUT를 보유하기위한 표준 싱글 톤을 간단히 만들면 요청간에 자동으로 지속됩니까? 아니면 특별히 응용 프로그램 상태로 푸시해야합니까?

자동으로 지속되면 응용 프로그램 상태와의 차이점은 무엇입니까?

올바른 싱글 톤 구현은 어떻게 생겼습니까? 느슨하게 초기화 할 필요는 없지만 스레드 안전성 (서버 인스턴스 당 수천 명의 이론적 사용자)이 있어야하며 성능이 좋습니다.

편집 : 존 소총의 4 번째 버전은 내가 정적 인 존재에 의존하지 않을 http://csharpindepth.com/Articles/General/Singleton.aspx

public sealed class Singleton 
{ 
    static readonly Singleton instance=new Singleton(); 

    // Explicit static constructor to tell C# compiler 
    // not to mark type as beforefieldinit 
    static Singleton() 
    { 
    } 

    Singleton() 
    { 
    } 

    public static Singleton Instance 
    { 
     get 
     { 
      return instance; 
     } 
    } 

    // randomguy's specific stuff. Does this look good to you? 
    private int[] lut = new int[5000]; 

    public int Compute(Product p) { 
     return lut[p.Goo]; 
    } 
} 

답변

2

예, 정적 멤버는 지속됩니다 (지속 된 것과 동일하지 않음 - "저장되지 않음, 결코 사라지지 않음). 이는 싱글 톤의 구현을 포함합니다. 정적 할당 또는 정적 생성자로 생성 된 것처럼 무료로 관련 정도가 낮은 초기화를 얻습니다. 관련 클래스가 처음 사용될 때까지 호출되지 않습니다. 이 생성은 기본적으로 잠금이지만 다른 모든 용도는 말한대로 스레드 세이프해야합니다. 동시성의 정도가 주어진다면 싱글 톤이 변경되지 않는 한 (룩업 테이블은 애플리케이션 수명 동안 변경되지 않습니다), 업데이트하는 방법에 관해서는 신중해야합니다 (한 가지 방법은 가짜 싱글 톤입니다 - 업데이트 할 때 새 객체를 만든 다음 현재 값을 바꿀 수 있도록 고정 시키십시오. "바깥에서"와 같이 보이지만 엄격하게 싱글 톤이 아닙니다.)

큰 위험은 전역 상태를 도입하는 모든 것이 용의주하다는 것입니다. 특히 웹과 같은 상태 비 저장 프로토콜을 사용할 때 특히 위험합니다. 특히 데이터베이스에서 쉽게 얻을 수없는 객체 그래프가 포함 된 경우에는 영구적 또는 거의 영구적 인 데이터의 메모리 내장 캐시로 사용하는 것이 좋습니다.

함정은 상당하므로주의해야합니다. 특히 잠금 문제의 위험을 과소 평가할 수는 없습니다.

편집은 문제의 편집에 맞게 :

내 큰 관심사는 배열이 초기화되는 방식이 될 것입니다. 분명히이 예는 불완전하기 때문에 각 항목마다 0이됩니다. 초기화시 설정되고 읽기 전용이면 괜찮습니다. 변경 가능하다면 스레딩에 대해 매우 신중해야합니다.

스케일링에 너무 많은 조회의 부정적인 영향을 알고 있어야합니다. 사전 계산을 사용하여 mosts 요청을 저장하는 동안 효과는 싱글 톤이 업데이트 될 때 매우 많은 작업 기간을 갖는 것입니다. long-ish 신생 기업은 (아주 자주는 아니지만) 용인 될 가능성이 있지만, 이후에 임의적 인 감속이 발생하면 출처를 추적하는 것이 까다로울 수 있습니다.

2

요청 사이에 지속 유망 보인다. [항상 요청이있을 때마다 프로세스가 재설정 될 가능성은 거의 없습니다.] HttpContext의 Cache 객체를 사용하여 요청간에 공유 리소스를 유지할 것을 권장합니다.

+0

HttpContext의 캐시는 응용 프로그램 도메인이 활성화되어있는 동안에 만 지속되므로 그 점에서 이점이 없습니다. 사실, 캐시는 영속성을 염두에두고 설계된 것이므로 동일한 목적을 제공하지는 않습니다 (중복되는 경우도 있습니다). –

0

편집 : 읽기 전용 잠금에 대한 Jon의 설명을 참조하십시오.

싱글 톤을 다루었으므로 오래되었습니다. (IOC 컨테이너 수명을 선호했습니다.)하지만 스레드 안전 문제를 처리하는 방법은 다음과 같습니다.싱글 톤의 상태를 변경시키는 모든 것을 잠글 필요가 있습니다. Compute(int)과 같이 읽기 전용 작업은 잠글 필요가 없습니다.

// I typically create one lock per collection, but you really need one per set of atomic operations; if you ever modify two collections together, use one lock. 
private object lutLock = new object(); 
private int[] lut = new int[5000]; 

public int Compute(Product p) { 
    return lut[p.Goo]; 
} 

public void SetValue(int index, int value) 
{ 
    //lock as little code as possible. since this step is read only we don't lock it. 
    if(index < 0 || index > lut.Length) 
    { 
     throw new ArgumentException("Index not in range", "index"); 
    } 
    // going to mutate state so we need a lock now 
    lock(lutLock) 
    { 
     lut[index] = value; 
    } 
} 
+0

필요는 없습니다. 이 경우에는 문제가되지는 않지만 Compute가 lut에서 하나 이상의 값을 필요로하는 경우 변경 전후의 값을 혼합하여 쉽게 사용할 수 있습니다. 그 응용 프로그램에 따라 괜찮거나 재앙이 될 수 있습니다. ReaderWriterLockSlim이 여기에 적합 할 수 있습니다. 또한 SetValue가 실제로 많은 값을 설정하는 Update 메서드 인 경우 새 값을 만든 다음 lut에 할당하는 것이 좋습니다. –

+0

Jon : 읽기 전용 작업에 잠금이 필요한 이유는 무엇입니까? 여러 개의 요청이 동시에 같은 메모리를 읽지 못하여 문제가 발생 했습니까? – randomguy

+0

@randomguy, 읽기 전용 연산 * do * 쓰기 작업이있는 경우 잠금이 필요합니다. 여기서는 Ryan과 의견이 다릅니다. 때때로 참조 또는 내장 함수의 단일 값 읽기에서 얻을 수는 있지만 메모리 장벽이 없기 때문에 오래된 컴퓨터 크기라면 문제가되지 않습니다 (스레드 캐시 메모리이므로 스레드 A가 쓴 후에 스레드 B가 읽으면 이전 값을 얻을 수 있습니다. 목적). 설정 후 유일한 작업이 읽기 인 경우 잠금이 필요 없습니다. 읽기는 상처를 입히지 만 글쓰기로 상처를 입지 않습니다. –