একাধিক সংস্থান সহ "ব্যবহার" করা কি কোনও উত্স ফাঁসের কারণ হতে পারে?


106

সি # আমাকে নিম্নলিখিতগুলি করতে দেয় (এমএসডিএন থেকে উদাহরণ):

using (Font font3 = new Font("Arial", 10.0f),
            font4 = new Font("Arial", 10.0f))
{
    // Use font3 and font4.
}

font4 = new Fontছুড়ে মারলে কী হয় ? আমি যা বুঝতে পারি তা থেকে ফন্ট 3 সংস্থানগুলি ফাঁস করবে এবং তা নিষ্পত্তি হবে না won't

  • এটা কি সত্য? (font4 নিষ্পত্তি হবে না)
  • using(... , ...)নেস্ট ব্যবহারের পক্ষে কি এর অর্থ পুরোপুরি এড়ানো উচিত?

7
এটি স্মৃতি ফাঁস করবে না ; সবচেয়ে খারাপ ক্ষেত্রে এটি এখনও জিসিডি পাবে।
স্ল্যাक्स

3
using(... , ...)নির্বিশেষে ব্লকগুলি ব্যবহার করে নীস্টে সংকলন করা থাকলে আমি অবাক হব না, তবে আমি নিশ্চিত তা জানি না।
ড্যান জে

1
যে আমি কি বোঝানো না. এমনকি আপনি যদি usingকিছুটা ব্যবহার না করেন তবে জিসি অবশেষে এটি সংগ্রহ করবে।
স্ল্যাक्स

1
@ জ্নাক: এটি যদি একটি একটি finallyব্লকে সংকলন করা হত , সমস্ত সংস্থান তৈরি না হওয়া অবধি এটি ব্লকে প্রবেশ করতে পারত না।
স্ল্যাक्स

2
@zneak: যেহেতু একটি রূপান্তর মধ্যে usingএকটি থেকে try- finally, আরম্ভের অভিব্যক্তি বাহিরে মূল্যায়ন করা হয় try। সুতরাং এটি একটি যুক্তিসঙ্গত প্রশ্ন।
বেন ভয়েগট

উত্তর:


158

না।

সংকলক 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

4
এটি সি # স্পেসিফিকেশন সংস্করণ 5.0, বিটিডব্লিউ-তে 8.13।
বেন ভয়েগট

11
@ ওয়েল্যান্ড ইয়ুটানি: আপনি কী জিজ্ঞাসা করছেন?
স্ল্যাक्स

9
@ ওয়েল্যান্ড ইয়ুটানি: এটি একটি প্রশ্নোত্তর সাইট। আপনার যদি প্রশ্ন থাকে তবে একটি নতুন প্রশ্ন শুরু করুন!
এরিক লিপার্ট

5
@ ব্যবহারকারী1306322 কেন? আমি যদি সত্যিই জানতে চাই?
অক্সিমারন

2
@ অক্সিমারন তারপরে আপনার গবেষণা এবং অনুমানের আকারে প্রশ্ন পোস্ট করার আগে আপনার প্রচেষ্টার কিছু প্রমাণ সরবরাহ করা উচিত, না হলে আপনাকে একই কথা বলা হবে, মনোযোগ হারাবেন এবং অন্যথায় আরও বড় ক্ষতি হতে হবে। ব্যক্তিগত অভিজ্ঞতার ভিত্তিতে কেবল একটি পরামর্শ।
ব্যবহারকারী1306322

67

আপডেট : আমি এই প্রশ্নটি এখানে পাওয়া যায় এমন নিবন্ধের ভিত্তি হিসাবে ব্যবহার করেছি ; এই সমস্যাটির অতিরিক্ত আলোচনার জন্য এটি দেখুন। ভাল প্রশ্নের জন্য ধন্যবাদ!


যদিও স্ক্যাবসের উত্তর অবশ্যই সঠিক এবং জিজ্ঞাসিত প্রশ্নের উত্তর দিয়েছে, আপনি আপনার জিজ্ঞাসা করেননি এমন প্রশ্নে একটি গুরুত্বপূর্ণ বৈকল্পিক রয়েছে:

কনস্ট্রাক্টর কর্তৃক অবহেলিত রিসোর্স বরাদ্দের পরে কিন্তু সিটি ফেরার আগে এবং রেফারেন্সটি পূরণ করার আগেfont4 = new Font() নিক্ষেপ করলে কী হবে ?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()ছুড়ে ফেলেছেন রিসোর্স বরাদ্দ হওয়ার আগে নিক্ষেপ ঘটে। অবজেক্ট বরাদ্দ করা হয়েছে তবে কর্টারটি আর ফিরে আসে না, তাই fooকখনও পূরণ হয় না We আমরা কখনও প্রবেশ tryকরি নি তাই আমরা কখনও প্রবেশ করি না finally। অবজেক্ট রেফারেন্স এতিম হয়েছে। শেষ পর্যন্ত জিসি এটি আবিষ্কার করবে এবং এটি চূড়ান্তকরণকারী সারিতে রাখবে। handleএখনও শূন্য, তাই চূড়ান্তকারী কিছুই না। লক্ষ্য করুন যে চূড়ান্ত করা হচ্ছে এমন কোনও অবজেক্টের মুখে ফাইনালাইজারটি দৃ rob় হতে হবে যার নির্মাণকারী কখনও শেষ করেনি । তুমি প্রয়োজনীয় finalizers যে এই শক্তিশালী লিখতে। এটি বিশেষজ্ঞের কাছে চূড়ান্ত রচনাকারী লেখা ছেড়ে দেওয়ার এবং এটি নিজে করার চেষ্টা না করার আরও একটি কারণ।

অনুমান করা Blah3() ছুড়ে ফেলেছেন সম্পদ বরাদ্দের পরে নিক্ষেপ ঘটে। তবে আবার fooকখনও পূরণ হয় না, আমরা কখনই প্রবেশ করি না finallyএবং চূড়ান্তকরণকারী থ্রেড দ্বারা বস্তুটি পরিষ্কার হয়ে যায়। এবার হ্যান্ডেলটি শূন্য নয়, এবং ফাইনালাইজারটি এটি পরিষ্কার করে। আবার, চূড়ান্তকরণটি এমন কোনও সামগ্রীতে চলছে যার নির্মাণকারী কখনও সফল হন নি, তবে ফাইনালাইজারটি যাইহোক চালায়। স্পষ্টতই এটি অবশ্যই হবে কারণ এই বারে এটি করার কাজ ছিল।

এখন ধরুন Blah2()নিক্ষেপ রিসোর্স বরাদ্দের পরে কিন্তু আগে ঘটে থ্রো হয় handle পূরণ হয়! আবার, ফাইনালাইজারটি চলবে তবে handleএখনও শূন্য এবং আমরা হ্যান্ডেলটি ফাঁস করি!

এই ফাঁসটি যাতে না ঘটে সে জন্য আপনাকে অত্যন্ত চতুর কোড লিখতে হবে । এখন, আপনার Fontসংস্থার ক্ষেত্রে, হেক কে যত্ন করে? আমরা একটি ফন্ট হ্যান্ডেল ফাঁস, বড় ব্যাপার। তবে যদি আপনি একেবারে ইতিবাচকভাবে প্রয়োজন যে প্রতিটি অপ্রয়োজনীয় রিসোর্স পরিষ্কার করা উচিত ব্যতিক্রমগুলির সময় নির্ধারণ করা যাই হোক না কেন আপনার হাতে আপনার খুব সমস্যা আছে।

সিএলআরকে লক দিয়ে এই সমস্যাটি সমাধান করতে হবে। যেহেতু সি # 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এ ভরা হয় যদি এবং কেবল যদি এটা deallocated করা প্রয়োজন।

এটি করার কৌশলগুলি বর্ণনা করা এই উত্তরের সুযোগের বাইরে। আপনার যদি এই প্রয়োজনীয়তা থাকে তবে একজন বিশেষজ্ঞের সাথে পরামর্শ করুন।


6
@gnat: গৃহীত উত্তর। যে এস কিছু জন্য দাঁড়িয়ে আছে। :-)
এরিক লিপার্ট

12
@Joe: অবশ্যই উদাহরণ হল কল্পিতআমি স্রেফ এটি রচনা করেছি । ঝুঁকিগুলি অতিরঞ্জিত করা হয় না কারণ আমি ঝুঁকির স্তর কী তা বলে নি ; বরং, আমি বলেছি যে এই প্যাটার্নটি সম্ভব । ক্ষেত্রটি স্থাপনের ফলে সমস্যাটি সরাসরি সমাধান করা যায় বলে আপনি বিশ্বাস করেন যে এই বিষয়টি সঠিকভাবে বোঝায়: যে বিপুল সংখ্যাগরিষ্ঠ প্রোগ্রামারদের মতো যাদের এই ধরণের সমস্যার অভিজ্ঞতা নেই, আপনি এই সমস্যাটি সমাধান করার উপযুক্ত নন; প্রকৃতপক্ষে, অধিকাংশ মানুষ এমনকি চিনতে না যে হয় একটি সমস্যা, যা কেন আমি প্রথম স্থানে এই উত্তরটি লিখেছেন
এরিক লিপার্ট

5
@ ক্রিস: ধরুন বরাদ্দ এবং রিটার্নের মধ্যে এবং রিটার্ন এবং অ্যাসাইনমেন্টের মধ্যে শূন্য কাজ শেষ হয়েছে। আমরা এই সমস্ত Blahপদ্ধতি কল মুছি। থ্রেডঅবর্টেক্সপশনকে points দফার যে কোনও একটিতে ঘটতে বাধা দেয় কী?
এরিক লিপার্ট

5
@ জো: এটি কোনও বিতর্কিত সমাজ নয়; আমি আরও দৃ being ়প্রত্যয়ী হয়ে পয়েন্ট সন্ধান করতে চাই না । যদি আপনি সংশয়বাদী হন এবং এর জন্য আমার কথাটি না নিতে চান যে এটি একটি জটিল সমস্যা যা সঠিকভাবে সমাধান করার জন্য বিশেষজ্ঞদের সাথে পরামর্শের প্রয়োজন হয় তবে আপনি আমার সাথে দ্বিমত পোষণ করতে স্বাগত।
এরিক লিপার্ট

7
@ গাইলস রবার্টস: এটি কীভাবে সমস্যার সমাধান করবে? ধরুন কলটি করার পরে ব্যতিক্রম ঘটেছিল AllocateResourceতবে তার আগে নিয়োগের আগেx । একটি ThreadAbortExceptionএ সময়ে ঘটতে পারে। এখানকার প্রত্যেকে আমার বক্তব্য অনুপস্থিত মনে হচ্ছে, যা কোনও উত্স তৈরি এবং ভেরিয়েবলের সাথে এটির রেফারেন্স নির্ধারণ করা কোনও পারমাণবিক ক্রিয়াকলাপ নয় । আমি চিহ্নিত সমস্যাটি সমাধান করার জন্য আপনাকে অবশ্যই এটি একটি পারমাণবিক অপারেশন করতে হবে।
এরিক লিপার্ট

32

@ এসএলক্স উত্তরের পরিপূরক হিসাবে, আপনার কোডের জন্য এখানে আইএল রয়েছে:

.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

নেস্টেড চেষ্টা / শেষ অবধি ব্লক নোট করুন।


17

এই কোড (মূল নমুনার উপর ভিত্তি করে):

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.
        }
    }
}

এটি নিম্নলিখিত সিআইএল উত্পাদন করে ( ভিজ্যুয়াল স্টুডিও ২০১৩ সালে , নেট .৪.৪.১ লক্ষ্য করে ):

.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()পদ্ধতিটি কল করে ।

মজার বিষয় হল, ডটপিকের সাহায্যে এই অ্যাসেম্বলিটি পচন করা নিম্নলিখিত পুনর্গঠিত উত্স তৈরি করে:

using System.Drawing;

public class Class1
{
    public Class1()
    {
        using (new Font("Arial", 10f))
        {
            using (new Font("Arial", 10f))
                ;
        }
    }
}

বিবর্তিত কোডটি নিশ্চিত করে যে সবকিছু ঠিক আছে এবং usingমূলত নাস্তে usings তে প্রসারিত । সিআইএল কোডটি দেখতে কিছুটা বিভ্রান্তিকর, এবং কী ঘটছে তা সঠিকভাবে বুঝতে পারার আগে আমাকে কয়েক মিনিটের জন্য এটি দেখতে হয়েছিল, তাই আমি অবাক হওয়ার কিছু নেই যে কিছু 'বৃদ্ধ স্ত্রীর গল্প' ছড়িয়ে পড়েছে about এই. যাইহোক, উত্পন্ন কোডটি অনুপলব্ধ সত্য।


@ পিটার মর্টেনসেন আপনার সম্পাদনা আইএল কোডের অংশগুলি (আইএল_0012 এবং আইএল_0017 এর মধ্যে) মুছে ফেলা হয়েছে এবং ব্যাখ্যাটি অবৈধ এবং বিভ্রান্তিকর উভয়ই উপস্থাপন করছে। এই কোডটি আমার প্রাপ্ত ফলাফলগুলির সম্পাদনার ভারব্যাটিম অনুলিপি হিসাবে তৈরি হয়েছিল এবং সম্পাদনাটি এটিকে অবৈধ করে। আপনি কি দয়া করে আপনার সম্পাদনা পর্যালোচনা করে নিশ্চিত করতে পারেন যে এটি আপনি কী ਚਾਹੁੰਦੇ?
টিম লং

7

@ এসএলক্সের উত্তরটি প্রমাণের জন্য এখানে একটি নমুনা কোড রয়েছে:

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);
    }
}

1
এটি প্রমাণ করে না। ডিসপোজ কোথায়: টি 2? :)
পাইটর পেরাক

1
প্রশ্নটি দ্বিতীয়টি নয় ব্যবহারের তালিকায় প্রথম উত্সের নিষ্পত্তি সম্পর্কে। " font4 = new Fontছুড়ে দিলে কী হয় ? আমি যা বুঝতে পেরেছি ফন্ট 3 রিসোর্স ফাঁস করবে এবং তা নিষ্পত্তি হবে না।"
wdosanjos
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.