[c#] ‘사용’문 대 ‘마지막으로 시도’

읽기 / 쓰기 잠금을 사용할 속성이 많이 있습니다. try finally또는 using절을 사용하여 구현할 수 있습니다 .

에서 try finally나는 이전에 잠금을 획득 try하고 finally. 에서 using절, 나는 그것의 폐기 방법에 생성자에서 잠금을 획득 클래스 및 자료를 만들 것입니다.

많은 곳에서 읽기 / 쓰기 잠금을 사용하고 있으므로 .NET보다 간결한 방법을 찾고 있습니다 try finally. 한 가지 방법이 권장되지 않는 이유 또는 다른 방법보다 더 나은 이유에 대한 몇 가지 아이디어를 듣고 싶습니다.

방법 1 ( try finally) :

static ReaderWriterLock rwlMyLock_m  = new ReaderWriterLock();
private DateTime dtMyDateTime_m
public DateTime MyDateTime
{
    get
    {
        rwlMyLock_m .AcquireReaderLock(0);
        try
        {
            return dtMyDateTime_m
        }
        finally
        {
            rwlMyLock_m .ReleaseReaderLock();
        }
    }
    set
    {
        rwlMyLock_m .AcquireWriterLock(0);
        try
        {
            dtMyDateTime_m = value;
        }
        finally
        {
            rwlMyLock_m .ReleaseWriterLock();
        }
    }
}

방법 2 :

static ReaderWriterLock rwlMyLock_m  = new ReaderWriterLock();
private DateTime dtMyDateTime_m
public DateTime MyDateTime
{
    get
    {
        using (new ReadLock(rwlMyLock_m))
        {
            return dtMyDateTime_m;
        }
    }
    set
    {
        using (new WriteLock(rwlMyLock_m))
        {
            dtMyDateTime_m = value;
        }
    }
}

public class ReadLock : IDisposable
{
    private ReaderWriterLock rwl;
    public ReadLock(ReaderWriterLock rwl)
    {
        this.rwl = rwl;
        rwl.AcquireReaderLock(0);
    }

    public void Dispose()
    {
        rwl.ReleaseReaderLock();
    }
}

public class WriteLock : IDisposable
{
    private ReaderWriterLock rwl;
    public WriteLock(ReaderWriterLock rwl)
    {
        this.rwl = rwl;
        rwl.AcquireWriterLock(0);
    }

    public void Dispose()
    {
        rwl.ReleaseWriterLock();
    }
}



답변

MSDN에서 문 사용 (C # 참조)

using 문은 개체에 대한 메서드를 호출하는 동안 예외가 발생하더라도 Dispose가 호출되도록합니다. 개체를 try 블록에 넣은 다음 finally 블록에서 Dispose를 호출하여 동일한 결과를 얻을 수 있습니다. 사실 이것은 컴파일러가 using 문을 번역하는 방법입니다. 앞의 코드 예제는 컴파일 타임에 다음 코드로 확장됩니다 (객체의 제한된 범위를 만드는 추가 중괄호에 유의하십시오).

{
  Font font1 = new Font("Arial", 10.0f);
  try
  {
    byte charset = font1.GdiCharSet;
  }
  finally
  {
    if (font1 != null)
      ((IDisposable)font1).Dispose();
  }
}

그래서 기본적으로, 동일한 코드이지만 멋진 자동 null 검사와 변수에 대한 추가 범위가 있습니다 . 또한 문서에는 “IDisposable 개체의 올바른 사용을 보장”한다고 명시되어 있으므로 향후 모호한 경우에 대해 더 나은 프레임 워크 지원을받을 수도 있습니다.

따라서 옵션 2로 이동하십시오.

더 이상 필요하지 않은 직후에 끝나는 범위 내에 변수 가있는 것도 장점입니다.


답변

나는 확실히 두 번째 방법을 선호합니다. 사용 시점에서 더 간결하고 오류 발생 가능성이 적습니다.

첫 번째 경우 코드를 편집하는 사람은 Acquire (Read | Write) Lock 호출과 try 사이에 아무것도 삽입하지 않도록주의해야합니다.

(이와 같은 개별 속성에 대해 읽기 / 쓰기 잠금을 사용하는 것은 일반적으로 과잉입니다. 훨씬 더 높은 수준에서 적용하는 것이 가장 좋습니다. 잠금이 유지되는 시간을 고려할 때 경합 가능성이 매우 작기 때문에 여기에서는 간단한 잠금으로 충분합니다. 읽기 / 쓰기 잠금을 획득하는 것은 단순 잠금보다 비용이 많이 드는 작업입니다.)


답변

두 솔루션 모두 예외를 가리기 때문에 나쁠 가능성을 고려하십시오 .

a가 try없는 것은 catch분명히 나쁜 생각입니다. 성명도 마찬가지로 위험한 이유는 MSDN 을 참조하십시오 using.

Microsoft는 이제 ReaderWriterLock 대신 ReaderWriterLockSlim 을 권장 합니다.

마지막으로 Microsoft 예제에서는 이러한 문제를 방지하기 위해 두 개의 try-catch 블록 을 합니다.

try
{
    try
    {
         //Reader-writer lock stuff
    }
    finally
    {
         //Release lock
    }
 }
 catch(Exception ex)
 {
    //Do something with exception
 }

간단하고 일관 적이며 깨끗한 솔루션이 좋은 목표이지만을 사용할 수 없다고 가정하면 lock(this){return mydateetc;}접근 방식을 재고 할 수 있습니다. 더 많은 정보를 통해 Stack Overflow가 도움이 될 수 있다고 확신합니다 😉


답변

저는 개인적으로 가능한 한 자주 C # “using”문을 사용하지만 언급 된 잠재적 인 문제를 피하기 위해 몇 가지 구체적인 작업을 수행합니다. 설명하기 위해 :

void doSomething()
{
    using (CustomResource aResource = new CustomResource())
    {
        using (CustomThingy aThingy = new CustomThingy(aResource))
        {
            doSomething(aThingy);
        }
    }
}

void doSomething(CustomThingy theThingy)
{
    try
    {
        // play with theThingy, which might result in exceptions
    }
    catch (SomeException aException)
    {
        // resolve aException somehow
    }
}

“using”문을 하나의 메서드로 분리하고 개체의 사용을 “try”/ “catch”블록을 사용하여 다른 메서드로 분리했습니다. 관련 객체에 대해 이와 같은 여러 “using”문을 중첩 할 수 있습니다 (가끔 프로덕션 코드에서 3 ~ 4 개 깊이로 이동합니다).

Dispose()이러한 사용자 정의에 대한 방법에서IDisposable 클래스, 나는 예외 (하지만 오류)를 잡아 (Log4net 사용)을 기록합니다. 이러한 예외가 내 처리에 영향을 미칠 수있는 상황을 본 적이 없습니다. 평소와 같이 잠재적 인 오류는 호출 스택을 전파하고 일반적으로 적절한 메시지 (오류 및 스택 추적)가 기록 된 상태로 처리를 종료 할 수 있습니다.

에서 중대한 예외가 발생할 수있는 상황이 발생하면 Dispose()해당 상황에 맞게 재 설계 할 것입니다. 솔직히 그런 일이 일어날 지 의심 스럽습니다.

한편, “사용”의 범위와 정리 이점은 제가 가장 좋아하는 C # 기능 중 하나입니다. 그건 그렇고, 저는 Java, C # 및 Python을 기본 언어로 사용하고 여기저기서 많은 다른 언어를 사용합니다. “사용”은 실용적이고 일상적인 일이기 때문에 가장 좋아하는 언어 기능 중 하나입니다. .


답변

세 번째 옵션이 좋아요

private object _myDateTimeLock = new object();
private DateTime _myDateTime;

public DateTime MyDateTime{
  get{
    lock(_myDateTimeLock){return _myDateTime;}
  }
  set{
    lock(_myDateTimeLock){_myDateTime = value;}
  }
}

두 가지 옵션 중 두 번째 옵션은 무슨 일이 일어나고 있는지 가장 깨끗하고 이해하기 쉽습니다.


답변

“속성 무리”및 속성 getter 및 setter 수준의 잠금이 잘못 보입니다. 잠금이 너무 세분화되어 있습니다. 대부분의 일반적인 객체 사용 에서 동시에 이상의 속성에 액세스 하기 위해 잠금을 획득했는지 확인하고 싶을 것 입니다. 귀하의 특정 사례는 다를 수 있지만 다소 의심 스럽습니다.

어쨌든, 속성 대신 객체에 액세스 할 때 잠금을 획득하면 작성해야하는 잠금 코드의 양을 크게 줄일 수 있습니다.


답변

DRY 말한다 : 두 번째 솔루션. 첫 번째 솔루션은 잠금 사용 논리를 복제하는 반면 두 번째 솔루션은 그렇지 않습니다.