2012-11-19 4 views
0

참고 아래 코드는 asp.net에서 가져온 것입니다.닫힌 개체의 가비지 수집


내가

AmazonS3 s3Client = Amazon.AWSClientFactory.CreateAmazonS3Client(); 

// ... 
// details elided 
// ... 

BackgroundWorker worker = new BackgroundWorker(); 

worker.DoWork += new DoWorkEventHandler((s, args) => 
{ 
    s3Client.PutObject(titledRequest); 
}); 

new Thread(() => worker.RunWorkerAsync()).Start(); 

아래 (잘못 작성된) 코드는 가비지 수집기가 백그라운드 작업자가 그것으로 끝날 때까지 결코, 이제까지 s3Client 개체를 수집 할만큼 똑똑 할 것인가가 있다면?


주, 난 단지 내가 직접 배경 노동자를 해고 할 때 발생 asp.net에서 제기됩니다 성가신 오류를 해결하기 위해 스레드 내부의 배경 노동자를 발로하고 있습니다.

+0

정확히. 이것은 GC가 필요한 것입니다. 스레드가 모든 참조를 잃을 때까지 s3client가 유지됩니다! – quetzalcoatl

+0

'RunWorkerAsync()'를 실행하는 데 새 스레드를 시작하는 이유는 무엇입니까? 먼저 비동기 적 방법입니다. 메서드의 내용은 새로운 스레드를 만들고, 조금만 구성한 다음 시작합니다. 백그라운드 스레드에서 그렇게 할 필요는 없습니다. 그렇게함으로써 무의미한 오버 헤드가 추가됩니다. 또한 lambdas를 사용할 때 사용되는 델리게이트를 정의 할 필요가 없으며'new DoWorkEventHandler'를 완전히 제거 할 수 있습니다. 문맥에 따라 암시 될 것입니다. – Servy

+0

@Servy 대리인을 정의 할 필요가 없다는 정보를 주셔서 감사합니다.다른 점은 backgroundWorker를 시작할 때 asp.net이 던져 버리는 엄청난 짜증나는 오류입니다. 주변을 검색하는 것으로부터 간단한 픽스가 아닌 것 같아서, 나는이 싸구려 해결책을 사용했다. –

답변

5

예, 가능합니다. 컴파일러는 클로저에서 참조하는 각 지역의 필드를 포함하는 새로운 클래스를 생성합니다. 클로저 바디는 해당 클래스의 메소드로 내보내지며 포함 함수의 모든 지역 객체는 해당 클로저 객체의 필드를 참조하도록 컴파일러에서 다시 작성됩니다.

이 모든 마법은 컴파일시 발생합니다. 런타임은 그것에 대해 아무 것도 알 필요가 없습니다. 런타임이 이미 위임 대상이되는 객체를 수집하지 않을 정도로 똑똑하기 때문에 클로저가 참조하는 로컬의 수명은 결과 대리자 객체의 수명까지 보장됩니다. 설명하기

이 컴파일러는이 같은 뱉어 것입니다 :

[System.Runtime.CompilerServices.CompilerGeneratedAttribute] 
internal class ClosureImplementation // See note 1 
{ 
    public AmazonS3 s3Client; 

    public void Method(object s, EventArgs args) 
    { 
     s3Client.PutObject(titledRequest); // See note 2 
    } 
} 

다음을 당신의 방법이 대신 방출 :

ClosureImplementation closure = new ClosureImplementation(); 
closure.s3Client = Amazon.AWSClientFactory.CreateAmazonS3Client(); 

// ... 

worker.DoWork += closure.Method; 

주 :

  1. 생성 된 클래스의 이름은 컴파일러에서 선택합니다. ClosureImplementation은 그 예입니다.
  2. titledRequest의 출처를 알 수없는 컨텍스트가 부족하므로 컴파일러의 처리 방법을 고의적으로 설명하지 않았습니다.
+0

와우 - 대단한 대답, 고마워. 그리고 나는 당신의 대답을 사랑했다. [here] (http://stackoverflow.com/a/4/477863/352552) btw : –

3

간단히 말해서, 그렇습니다.

이벤트 처리기에 람다를 사용하고 있으며 로컬 변수를 닫고 있습니다. 즉, 람다가 정의 된 블록 외부의 범위에서 변수를 사용하고 있음을 의미합니다. 즉, 람다가 컴파일러에 의해 "실제"메소드 (클래스 내부에서 실제 이름과 객체 인스턴스 및 모두) 해당 클래스의 필드로 닫힌 지역 변수에 대한 참조를 포함합니다. 이것은 람다가 더 이상 어디에서도 참조되지 않고 어디서나 실행될 때까지 해당 객체를 범위에 유지합니다.

+0

이것은 람다식이 아닌 익명의 위임자들에게도 적용된다. – cdhowie

+0

@cdhowie 맞습니다. 익명의 대리자를 쓸모없는 것으로 만들기 위해 람다 (lambdas)를 고려합니다. 실제로는 더 이상 필요하지 않으며, 계속 사용하는 사람들은 거의 없습니다. – Servy

+0

Lambda에는 "예상되는 인수 유형과 일치하고 사용하지 마십시오"라는 구문이 없습니다. 나는'delegate {...}'을 사용하는데, 그 이유는 lambda에 대한 동일한 문법이 없기 때문입니다. 람다를 사용하면 인수 목록을 제공해야합니다. 또한 람다는 가능한 경우 '표현식'기반의 오버로드 메소드를 선호하며, 그렇지 않으면 델리게이트 기반의 오버로드로 대체됩니다. 경우에 따라서는'Expression' 오버로드 대신 델리게이트 오버로드를 사용하도록 강요하는 것이 바람직 할 수 있으므로이 경우 익명의 위임 구문을 사용하는 것이 좋습니다. – cdhowie