둘 이상의 리소스를 "사용"하면 리소스 누수가 수 있습니까?
C #을 사용하면 다음을 수행 할 수 있습니다 (MSDN의 예).
using (Font font3 = new Font("Arial", 10.0f),
font4 = new Font("Arial", 10.0f))
{
// Use font3 and font4.
}
하면 font4 = new Font
어떻게합니까? 내가 이해하는 바에서 font3는 리소스를 폐기하고 않을 것입니다.
- 이것이 사실입니까? (font4는 폐기되지 않음)
- 이것은
using(... , ...)
중첩 사용을 위해 아예 피해야 하는 것을 의미합니까 ?
아니.
컴파일러는 각 finally
변수에 대해 별도의 블록을 생성 합니다.
(§8.13)는 말한다 사양 :
자원 금액이 지역 변수 선언의 형태를 취하면 주어진 유형의 여러 자원을 금액 할 수 있습니다.
using
양식 의 진술using (ResourceType r1 = e1, r2 = e2, ..., rN = eN) statement
문 시퀀스와 정확히 동일합니다.
using (ResourceType r1 = e1) using (ResourceType r2 = e2) ... using (ResourceType rN = eN) statement
업데이트 : 나는 여기 에서 사용할 수있는 기사의 기초 로이 질문을 사용했다 ; 이 문제에 대한 추가 논의는 그것을 참조하십시오. 좋은 질문에 감사드립니다!
Schabse의 답변 은 물론이고 질문에 대한 답변 이지만 질문하지 중요한 변형이 있습니다.
어떻게됩니까하면
font4 = new Font()
발생 후 관리되지 않는 리소스가 생성자가 아니라 할당 된 전 에서의 ctor에 채우기 및 반환font4
기준으로?
좀 더 준비하겠습니다. 다음이 안심 가정합니다.
public sealed class Foo : IDisposable
{
private int handle = 0;
private bool disposed = false;
public Foo()
{
Blah1();
int x = AllocateResource();
Blah2();
this.handle = x;
Blah3();
}
~Foo()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (!this.disposed)
{
if (this.handle != 0)
DeallocateResource(this.handle);
this.handle = 0;
this.disposed = true;
}
}
}
이제 우리는
using(Foo foo = new Foo())
Whatever(foo);
이
{
Foo foo = new Foo();
try
{
Whatever(foo);
}
finally
{
IDisposable d = foo as IDisposable;
if (d != null)
d.Dispose();
}
}
확인. 던졌다 고 가정 Whatever
합니다. 그런 다음 finally
블록이 실행되고 리소스가 할당 해제됩니다. 문제 없어요.
던졌다 고 가정 Blah1()
합니다. 그런 다음 리소스가 할당되기 전에 발생합니다. ctor는 foo
받을 수 있습니다. 는 절대로 입력 입력 우리하지 않았 try
으므로 finally
어느 것도 입력 입력하지 않습니다 . 개체 참조가 분리되었습니다. 결국 GC는이를 발견하여 종료 자에 넣습니다. handle
여전히 0 종료자는 아무 작업도 수행하지 않습니다. 종료자는 생성자가 완료되지 않은 상태에서 종료되어야합니다 . 은있다 당신 필요한 이 강한 파이널 라이저를 작성. 이것이 최종 결정 해 전문가에게 맡기고 직접 시도 안되는 또 다른 이유입니다.
던졌다 고 가정 Blah3()
합니다. 리소스가 할당 된 후에 발생합니다. 그러나 다시, foo
채워지지 않고, 우리는 절대로 입력하지 않고, 우리는 절대로 입력하지 finally
않고 정리됩니다. 이번에는 핸들이 0이 아니라 조용히 정리합니다. 다시 말하지만, 종료자는 종료자는 성공한 적이없는 경우 실행됩니다. 이번에는 반드시해야 할 일이 있었기 때문에해야합니다.
이제 던졌다 고 가정 Blah2()
합니다. 자원이 전에 할당 된 후, 채워 지기 전에 발생합니다 handle
! 다시 말하지만, 파이널 라이저가 실행 지금 handle
은 여전히 0이고 핸들이됩니다!
이 많은이 발생하지 않을 경우 매우 영리한 코드 를 작성해야합니다 . 자, 당신의 Font
자원 의 경우 도대체 누가 신경 쓰나요? 글꼴 처리가됩니다. 그러나 예외적 인 경우에 관계없이 관리되지 않는 모든 리소스를 정리 하지 않고 요구 하는 경우 매우 어려운 문제가 발생합니다.
CLR은 잠금 으로이 문제를 해결해야합니다. C # 4 이후 lock
문 을 사용하는 잠금은 다음과 같이 구현되었습니다.
bool lockEntered = false;
object lockObject = whatever;
try
{
Monitor.Enter(lockObject, ref lockEntered);
lock body here
}
finally
{
if (lockEntered) Monitor.Exit(lockObject);
}
Enter
매우 신중하게 그렇게 기록 된 예외가 행사 상관없이가 , lockEntered
true로 설정 하고있는 경우에만 잠금이 촬영했다. 요구 사항이있는 경우 다음과 같이 작성해야합니다.
public Foo()
{
Blah1();
AllocateResource(ref handle);
Blah2();
Blah3();
}
그리고 내부 에서 무슨 일이 일어나고 할당 을 해제해야 할 때만 채워지 도록 AllocateResource
영리하게 작성하십시오 .Monitor.Enter
AllocateResource
handle
그렇게하는 기술을 설명하는 것은 답변의 범위를 벗어납니다. 이 사항이 있다면 전문가에게 문의하십시오.
@SLaks 답변을 보완하기 위해 코드에 대한 IL은 다음과 가변합니다.
.method private hidebysig static
void Main (
string[] args
) cil managed
{
// Method begins at RVA 0x2050
// Code size 74 (0x4a)
.maxstack 2
.entrypoint
.locals init (
[0] class [System.Drawing]System.Drawing.Font font3,
[1] class [System.Drawing]System.Drawing.Font font4,
[2] bool CS$4$0000
)
IL_0000: nop
IL_0001: ldstr "Arial"
IL_0006: ldc.r4 10
IL_000b: newobj instance void [System.Drawing]System.Drawing.Font::.ctor(string, float32)
IL_0010: stloc.0
.try
{
IL_0011: ldstr "Arial"
IL_0016: ldc.r4 10
IL_001b: newobj instance void [System.Drawing]System.Drawing.Font::.ctor(string, float32)
IL_0020: stloc.1
.try
{
IL_0021: nop
IL_0022: nop
IL_0023: leave.s IL_0035
} // end .try
finally
{
IL_0025: ldloc.1
IL_0026: ldnull
IL_0027: ceq
IL_0029: stloc.2
IL_002a: ldloc.2
IL_002b: brtrue.s IL_0034
IL_002d: ldloc.1
IL_002e: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_0033: nop
IL_0034: endfinally
} // end handler
IL_0035: nop
IL_0036: leave.s IL_0048
} // end .try
finally
{
IL_0038: ldloc.0
IL_0039: ldnull
IL_003a: ceq
IL_003c: stloc.2
IL_003d: ldloc.2
IL_003e: brtrue.s IL_0047
IL_0040: ldloc.0
IL_0041: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_0046: nop
IL_0047: endfinally
} // end handler
IL_0048: nop
IL_0049: ret
} // end of method Program::Main
중첩 된 try / finally 블록에 유의하십시오.
이 코드 (원래 샘플 기반) :
using System.Drawing;
public class Class1
{
public Class1()
{
using (Font font3 = new Font("Arial", 10.0f),
font4 = new Font("Arial", 10.0f))
{
// Use font3 and font4.
}
}
}
다음 CIL을 생성합니다 ( Visual Studio 2013 에서 .NET 4.5.1을 대상으로 함 ).
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
// Code size 82 (0x52)
.maxstack 2
.locals init ([0] class [System.Drawing]System.Drawing.Font font3,
[1] class [System.Drawing]System.Drawing.Font font4,
[2] bool CS$4$0000)
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: nop
IL_0007: nop
IL_0008: ldstr "Arial"
IL_000d: ldc.r4 10.
IL_0012: newobj instance void [System.Drawing]System.Drawing.Font::.ctor(string,
float32)
IL_0017: stloc.0
.try
{
IL_0018: ldstr "Arial"
IL_001d: ldc.r4 10.
IL_0022: newobj instance void [System.Drawing]System.Drawing.Font::.ctor(string,
float32)
IL_0027: stloc.1
.try
{
IL_0028: nop
IL_0029: nop
IL_002a: leave.s IL_003c
} // end .try
finally
{
IL_002c: ldloc.1
IL_002d: ldnull
IL_002e: ceq
IL_0030: stloc.2
IL_0031: ldloc.2
IL_0032: brtrue.s IL_003b
IL_0034: ldloc.1
IL_0035: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_003a: nop
IL_003b: endfinally
} // end handler
IL_003c: nop
IL_003d: leave.s IL_004f
} // end .try
finally
{
IL_003f: ldloc.0
IL_0040: ldnull
IL_0041: ceq
IL_0043: stloc.2
IL_0044: ldloc.2
IL_0045: brtrue.s IL_004e
IL_0047: ldloc.0
IL_0048: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_004d: nop
IL_004e: endfinally
} // end handler
IL_004f: nop
IL_0050: nop
IL_0051: ret
} // end of method Class1::.ctor
보시다시피 try {}
블록은에서 발생하는 첫 번째 할당 이후까지 시작되지 않습니다 IL_0012
. 언뜻보기에 이것은 보호되지 않은 코드에서 첫 번째 항목을 할당하는 것처럼 보입니다 . 그러나 결과는 위치 0에 저장됩니다. 두 번째 할당이 실패하면 외부 finally {}
블록이 실행되고 이는 위치 0, 즉의 첫 번째 할당에서 객체를 가져와 font3
해당 Dispose()
메서드를 호출합니다 .
흥미롭게도 dotPeek 로이 어셈블리를 디 컴파일 하면 다음과 같은 재구성 된 소스가 생성됩니다.
using System.Drawing;
public class Class1
{
public Class1()
{
using (new Font("Arial", 10f))
{
using (new Font("Arial", 10f))
;
}
}
}
디 컴파일 된 코드는 모든 것이 정확하고 using
기본적으로 중첩 된 using
s 로 확장 되는지 확인합니다 . CIL 코드는보기에 약간 혼란스러워서 무슨 일이 일어나고 있는지 제대로 이해하기 전에 몇 분 동안 쳐다보아야했기 때문에 일부 '늙은 아내 이야기'가 싹트기 시작했다는 사실에 놀랍지 않습니다. 이. 그러나 생성 된 코드는 공격 할 수없는 진실입니다.
다음은 @SLaks 답변을 증명하는 샘플 코드입니다.
void Main()
{
try
{
using (TestUsing t1 = new TestUsing("t1"), t2 = new TestUsing("t2"))
{
}
}
catch(Exception ex)
{
Console.WriteLine("catch");
}
finally
{
Console.WriteLine("done");
}
/* outputs
Construct: t1
Construct: t2
Dispose: t1
catch
done
*/
}
public class TestUsing : IDisposable
{
public string Name {get; set;}
public TestUsing(string name)
{
Name = name;
Console.WriteLine("Construct: " + Name);
if (Name == "t2") throw new Exception();
}
public void Dispose()
{
Console.WriteLine("Dispose: " + Name);
}
}
'IT' 카테고리의 다른 글
Android 애플리케이션에서 datetime이 'now'로 집합 SQLite 레코드를 삽입하는 방법은 무엇입니까? (0) | 2020.08.09 |
---|---|
IE에서 Flexbox가 세로로 가운데에 발생하지 않음 (0) | 2020.08.09 |
유효한 정수인지 테스트 (0) | 2020.08.09 |
자바 펼쳐가 입력에 점점이 맞는지 감지 (0) | 2020.08.09 |
AngularJS ng-repeat에서 반복되는 요소의 계산 (0) | 2020.08.09 |