2011-02-17 5 views
8

MSDN에서는 BCL 유형의 인스턴스 멤버에 대한 스레드 안전성을 문서화하고 있지만 실제로 IDisposable 형식의 Dispose 메서드를 호출하는 방법을 나타내는 정보는 호출하지 못했습니다.쓰레기 처리 방법의 안전성?

Dispose 메서드는 a) 모든 클래스에 대해 스레드로부터 안전 할 수 있으며, b) 스레드로부터 안전하다는 보장이 없으며, c) 일부 클래스에 대해 스레드로부터 안전하다는 보장이있는 경우 (구체적으로 설명되어있는 경우))?

마지막으로 Dispose 메서드가 스레드로부터 안전하다는 보장이 있으면 일회용 리소스를 사용하는 클래스의 각 인스턴스 메서드 주위에 잠금을 설정해야한다는 의미입니까?

요점 : 유형에 대한 파이널 라이저는 가비지 수집이 .NET에서 (매우 적극적으로) 작동하는 방식으로 인해 스레드로부터 안전해야하며 잠재적으로 Dispose 메소드를 호출 할 수 있음을 알고 있습니다. 그러나 여기에서이 문제를 제쳐두겠습니다.

+0

아마도 도움이 될 수 있습니다. http://stackoverflow.com/questions/151000/finalizers-and-dispose. –

+0

고마워요,하지만 그건 제가 묻고있는 것이 아닙니다. 더욱이, 나는 파이널 라이저를 전혀 신경 쓰지 않는다. – Noldorin

+0

다시 말하자면 명시 적으로 'Dispose'를 호출하고 Finalizer 스레드에 의존하지 말아야합니까? –

답변

6

thread-safety 및 Dispose 문제는 다소 까다 롭습니다. 많은 경우에 다른 스레드가 처분을 시작하면 어떤 스레드가 객체와 합법적으로 할 수있는 유일한 방법이기 때문에 스레드 자체를 처분하려는 시도입니다. 처음에는 스레드 안전성을 보장하는 데 필요한 유일한 것처럼 보입니다. Interlocked.Exchange를 '처분'플래그로 사용하여 한 스레드의 Dispose 시도가 발생하고 다른 스레드가 Dispose 시도가 자동으로 무시되도록합니다. 사실 그것은 좋은 출발점이며 표준 Dispose 패턴의 일부였던 것 같습니다. (CompareExchange는 봉인 된 기본 클래스 래퍼 메서드에서 수행되어 모든 파생 클래스가 자체 비공개 클래스를 사용할 필요가 없도록해야합니다. 처분 플래그). 불행히도 Dispose가 실제로하는 일을 고려할 때, 상황은 훨씬 더 복잡합니다.

Dispose의 진정한 목적은 폐기되는 개체에 대해 무언가를하는 것이 아니라 해당 개체가 참조를 보유하고있는 다른 개체를 정리하는 것입니다. 이러한 엔티티는 관리 객체, 시스템 객체 또는 완전히 다른 것일 수 있습니다. 그들은 심지어 처분되는 대상과 같은 컴퓨터에 있지 않을 수도 있습니다. Dispose가 스레드로부터 안전하려면 다른 엔티티가 다른 스레드가 다른 작업을 수행하는 것과 동시에 Dispose가이를 정리하도록 허용해야합니다. 일부 객체는 이러한 사용을 처리 할 수 ​​있습니다. 다른 사람들은 할 수 없다.

하나의 특별한 난처한 예 : 객체는 스레드로부터 안전하지 않은 RemoveHandler 메소드로 이벤트를 가질 수 있습니다. 따라서 이벤트 처리기를 정리하는 모든 Dispose 메서드는 구독이 만들어진 스레드와 동일한 스레드에서만 호출해야합니다.

+0

응답 해 주셔서 감사합니다. 참으로 복잡한 문제입니다. 나는 당신이'Interlocked.Increment' 제안에 당신을 데려 갈 것이라고 생각합니다. – Noldorin

2

페이지는 MSDN here에 실제로 Dispose 메소드가 threadsafe가 아니라는 것을 절대 알지 못하지만, 나의 독서에는 그 코드가 아니므로 threadafe가 아니며 필요할 경우이를 고려해야한다는 것을 의미합니다. 예제 코드에서

특히 코멘트 :

// This class shows how to use a disposable resource. 
// The resource is first initialized and passed to 
// the constructor, but it could also be 
// initialized in the constructor. 
// The lifetime of the resource does not 
// exceed the lifetime of this instance. 
// This type does not need a finalizer because it does not 
// directly create a native resource like a file handle 
// or memory in the unmanaged heap. 

public class DisposableResource : IDisposable 
{ 

    private Stream _resource; 
    private bool _disposed; 

    // The stream passed to the constructor 
    // must be readable and not null. 
    public DisposableResource(Stream stream) 
    { 
     if (stream == null) 
      throw new ArgumentNullException("Stream in null."); 
     if (!stream.CanRead) 
      throw new ArgumentException("Stream must be readable."); 

     _resource = stream; 

     _disposed = false; 
    } 

    // Demonstrates using the resource. 
    // It must not be already disposed. 
    public void DoSomethingWithResource() { 
     if (_disposed) 
      throw new ObjectDisposedException("Resource was disposed."); 

     // Show the number of bytes. 
     int numBytes = (int) _resource.Length; 
     Console.WriteLine("Number of bytes: {0}", numBytes.ToString()); 
    } 


    public void Dispose() 
    { 
     Dispose(true); 

     // Use SupressFinalize in case a subclass 
     // of this type implements a finalizer. 
     GC.SuppressFinalize(this);  
    } 

    protected virtual void Dispose(bool disposing) 
    { 
     // If you need thread safety, use a lock around these 
     // operations, as well as in your methods that use the resource. 
     if (!_disposed) 
     { 
      if (disposing) { 
       if (_resource != null) 
        _resource.Dispose(); 
        Console.WriteLine("Object disposed."); 
      } 

      // Indicate that the instance has been disposed. 
      _resource = null; 
      _disposed = true; 
     } 
    } 
} 
+0

그래, 나는 이것을 알아 챘다. 흥미로운 예입니다. 불행히도 문서 작성자는 항상 BCL 프로그래머와 동등한 것은 아니며 BCL 유형의 경우에 대해서는 명시 적으로 설명하지 않습니다. 나는 당신이 보증의 일반적인 부족에 관하여다는 것을 의심한다. – Noldorin

+0

관련 메모에서 Dispose 메서드가 스레드로부터 안전하면 일회용 리소스를 사용하는 모든 메서드 (즉, if (_disposed) 검사를 수행하는 메서드)가 삭제와 관련하여 스레드로부터 안전하지 않아야합니다 (즉, 잠금)? – Noldorin

+1

@ Noldorin : 그 이유는 내가 99.999 ... 이유는 문서가 스레드 안전성을 논의 할 때 '인스턴스 멤버'에 'Dispose()'가 포함되어 있다고 확신하기 때문입니다. 'Dispose()'*는 인스턴스 멤버 *이므로, 나머지와 같은 thread-safety 속성을 가져야합니다. –

2

나는 별도로 명시하지 않는 한, 모든 클래스의 Dispose() 방법은 스레드 안전을 나타내는 문서의 목적을 위해 '인스턴스 멤버'로 계산 것이라고 확신 해 아닙니다.

따라서 설명서에 인스턴스 멤버가 스레드로부터 안전하지 않다고 나와 있으면 Dispose()이 나머지와 다른 것으로 명시되어 있지 않는 한 해당 인스턴스가 필요하지 않습니다.

+0

나는 이것처럼 단순한 것들을 희망한다! 사실, Dispose는 인스턴스 메서드이지만 명시적인 선언이 부족하지는 않습니다. 이 말을 듣고 .NET Reflector를 사용하여 몇 가지 예제를 파헤쳐 볼 수 있습니다 ... – Noldorin

+0

좀 더 구체적으로 말하자면, 제가 고려하고있는 경우 중 하나는 위에있는 'Socket'또는 'Stream' 개체의 읽기 작업 중에 * 한 스레드, 다른 스레드에서'Dispose '호출. 여기서 어떻게됩니까? 읽기가 자동으로 계속 진행되거나 손상된 상태가되거나 예외가 발생합니까? – Noldorin

+0

클래스가 인스턴스 메서드에서 스레드 안전성을 보장하지 않으면 예외가 발생하지 않아야합니다. 결과가 무엇인지 전혀 알 수 없습니다. –