UPDATE : ผมใช้คำถามนี้เป็นพื้นฐานสำหรับบทความซึ่งสามารถพบได้นั้นที่นี่ ; ดูการอภิปรายเพิ่มเติมเกี่ยวกับปัญหานี้ ขอบคุณสำหรับคำถามดีๆ!
แม้ว่าคำตอบของ 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 จะค้นพบและวางไว้ในคิว Finalizer handle
ยังคงเป็นศูนย์ดังนั้น Finalizer จึงไม่ทำอะไรเลย ขอให้สังเกตว่า finalizer จะต้องมีประสิทธิภาพในใบหน้าของวัตถุที่มีการสรุปที่มีตัวสร้างไม่เสร็จ คุณจะต้องเขียน Finalizers ที่มีความแข็งแกร่งนี้ นี่เป็นอีกเหตุผลหนึ่งที่คุณควรเขียนบทสรุปให้กับผู้เชี่ยวชาญและอย่าพยายามทำด้วยตัวเอง
สมมติว่าBlah3()
พ่น การโยนเกิดขึ้นหลังจากจัดสรรทรัพยากร แต่อีกครั้งfoo
ไม่เคยกรอกเราไม่เคยเข้าfinally
และวัตถุจะถูกล้างโดยเธรดสุดท้าย คราวนี้แฮนเดิลไม่ใช่ศูนย์และ Finalizer จะทำความสะอาด อีกครั้ง Finalizer กำลังทำงานบนวัตถุที่ตัวสร้างไม่เคยประสบความสำเร็จ แต่ Finalizer จะทำงานต่อไป แน่นอนว่ามันต้องเป็นเพราะครั้งนี้มันมีงานที่ต้องทำ
ตอนนี้สมมติว่าBlah2()
พ่น การโยนเกิดขึ้นหลังจากจัดสรรทรัพยากร แต่ก่อน handle
จะเต็ม! อีกครั้ง Finalizer จะทำงาน แต่ตอนนี้handle
ยังคงเป็นศูนย์และเรารั่วที่จับ!
คุณต้องเขียนโค้ดที่ฉลาดมากเพื่อป้องกันการรั่วไหลนี้ ตอนนี้ในกรณีของ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
กำหนดเป็นจริงถ้าหากล็อคถูกนำตัวจริง หากคุณมีข้อกำหนดที่คล้ายกันสิ่งที่คุณต้องเขียนคือ:
public Foo()
{
Blah1();
AllocateResource(ref handle);
Blah2();
Blah3();
}
และเขียนAllocateResource
อย่างชาญฉลาดMonitor.Enter
เพื่อที่ว่าไม่ว่าจะเกิดอะไรขึ้นภายในAllocateResource
สิ่งhandle
นั้นจะถูกเติมในกรณีที่จำเป็นต้องถูกจัดสรรเท่านั้น
การอธิบายเทคนิคในการทำเช่นนั้นอยู่นอกเหนือขอบเขตของคำตอบนี้ ปรึกษาผู้เชี่ยวชาญหากคุณมีข้อกำหนดนี้