একটি অ্যাসিঙ্ক শূন্য পদ্ধতিতে ছুঁড়ে দেওয়া ব্যতিক্রম ধরুন


282

.NET- র জন্য মাইক্রোসফ্ট থেকে অ্যাসিঙ্ক সিটিপি ব্যবহার করে, কলিং পদ্ধতিতে অ্যাসিঙ্ক পদ্ধতিতে ছুঁড়ে দেওয়া ব্যতিক্রম ধরা কি সম্ভব?

public async void Foo()
{
    var x = await DoSomethingAsync();

    /* Handle the result, but sometimes an exception might be thrown.
       For example, DoSomethingAsync gets data from the network
       and the data is invalid... a ProtocolException might be thrown. */
}

public void DoFoo()
{
    try
    {
        Foo();
    }
    catch (ProtocolException ex)
    {
          /* The exception will never be caught.
             Instead when in debug mode, VS2010 will warn and continue.
             The deployed the app will simply crash. */
    }
}

সুতরাং মূলত আমি এসেইঙ্ক কোড থেকে ব্যতিক্রমটি আমার কলিং কোডে বুদবুদ করতে চাই যদি তা এমনকি সম্ভবও হয়।


1
এটি আপনাকে কোনও সহায়তা দেয়? সামাজিক.এমএসডিএন.মাইক্রোসফট.ফরমেশনস
এএন /

22
ভবিষ্যতে কেউ যদি এ নিয়ে হোঁচট খায় তবে অ্যাসিঙ্ক / অ্যাওয়েট সেরা অভ্যাসগুলি ... নিবন্ধটির "অ্যাসিন্কের শূন্যপদ পদ্ধতি থেকে চিত্র 2 ব্যতীত ধরা পড়তে পারে না" তে এর ভাল ব্যাখ্যা রয়েছে। " যখন অ্যাসিঙ্ক টাস্ক বা অ্যাসিঙ্ক টাস্ক <T> পদ্ধতি থেকে কোনও ব্যতিক্রম ছুঁড়ে ফেলা হয়, তখন সেই ব্যতিক্রমটি টাস্ক অবজেক্টে ক্যাপচার করা হয় এবং স্থাপন করা হয় as এসিঙ্ক শূন্য পদ্ধতিগুলির সাথে, কোনও টাস্ক অবজেক্ট থাকে না, কোনও ব্যতিক্রম একটি অ্যাসিঙ্ক শূন্য পদ্ধতিতে ফেলে দেওয়া হয় ceptions অ্যাসিঙ্ক শূন্য পদ্ধতি শুরু করার সময় সক্রিয় ছিল এমন সিঙ্ক্রোনাইজেশন কনটেক্সটগুলিতে সরাসরি উত্থাপিত হবে। "
মিঃ মূস

উত্তর:


262

এটি পড়তে কিছুটা অদ্ভুত তবে হ্যাঁ, ব্যতিক্রমটি কলিং কোডে বুদবুদ হবে - তবে আপনি awaitবা আপনি যদি Wait()কল করেন তবেইFoo

public async Task Foo()
{
    var x = await DoSomethingAsync();
}

public async void DoFoo()
{
    try
    {
        await Foo();
    }
    catch (ProtocolException ex)
    {
          // The exception will be caught because you've awaited
          // the call in an async method.
    }
}

//or//

public void DoFoo()
{
    try
    {
        Foo().Wait();
    }
    catch (ProtocolException ex)
    {
          /* The exception will be caught because you've
             waited for the completion of the call. */
    }
} 

অ্যাসিঙ্ক শূন্য পদ্ধতিতে ত্রুটি-হ্যান্ডলিং বিভিন্ন শব্দার্থবিজ্ঞান রয়েছে। যখন একটি ব্যতিক্রম কোনও এসিঙ্ক টাস্ক বা অ্যাসিঙ্ক টাস্ক পদ্ধতি থেকে ছিটকে যায়, তখন সেই ব্যতিক্রমটি টাস্ক অবজেক্টে ক্যাপচার এবং স্থাপন করা হয়। অ্যাসিঙ্ক শূন্য পদ্ধতিগুলির সাথে, কোনও টাস্ক অবজেক্ট নেই, সুতরাং অ্যাসিঙ্ক শূন্য পদ্ধতিতে যে কোনও ব্যতিক্রম সরাসরি সিঙ্ক্রোনাইজেশন কনটেক্সটে উত্থাপিত হবে যা অ্যাসিঙ্ক শূন্য পদ্ধতিটি শুরু হওয়ার পরে সক্রিয় ছিল। - https://msdn.microsoft.com/en-us/magazine/jj991977.aspx

নোট করুন যে অপেক্ষা () ব্যবহারের ফলে আপনার অ্যাপ্লিকেশনটি ব্লক হয়ে যেতে পারে, যদি নেট আপনার সিঙ্ক্রোনালি পদ্ধতিটি কার্যকর করার সিদ্ধান্ত নেয়।

এই ব্যাখ্যাটি http://www.interact-sw.co.uk/iangblog/2010/11/01/csharp5-async-exferences বেশ ভাল - এটি এই যাদুটি অর্জনে সংকলকটি কী পদক্ষেপ নেয় তা নিয়ে আলোচনা করে।


3
আমি বলতে চাইছি এটি পড়তে সোজা-এগিয়ে - যদিও আমি জানি যে আসলে কী চলছে তা সত্যই জটিল - তাই আমার মস্তিষ্ক আমাকে আমার চোখকে বিশ্বাস না করার জন্য বলছে ...
স্টুয়ার্ট

8
আমি মনে করি ফু () পদ্ধতিটি শূন্যতার পরিবর্তে টাস্ক হিসাবে চিহ্নিত করা উচিত।
সর্নিই

4
আমি নিশ্চিত যে এটি একটি সমষ্টিগত ধারণা তৈরি করবে। এই হিসাবে, এই উত্তরে প্রদর্শিত ক্যাচ ব্লকটি ব্যতিক্রমটিকে ধরবে না।
xanadont

2
"তবে কেবলমাত্র যদি আপনি অপেক্ষা করেন বা অপেক্ষা করুন () ফোনে কল" " awaitযখন ফু শূন্য হয়ে যাচ্ছেন তখন আপনি কীভাবে ফোনে কল করতে পারবেন ? async void Foo()Type void is not awaitable?
রিজম

3
অকার্যকর পদ্ধতির অপেক্ষায় থাকা যায় না, তাই না?
হিতেশ পি

74

ব্যতিক্রম ধরা না পড়ার কারণ হ'ল Foo () পদ্ধতিতে একটি শূন্য রিটার্ন টাইপ থাকে এবং তাই যখন অপেক্ষা করা হয় তখন তা ফিরে আসে। যেহেতু ডুফু () ফু এর সমাপ্তির অপেক্ষায় নয়, ব্যতিক্রম হ্যান্ডলারটি ব্যবহার করা যাবে না।

এই সহজ কোনো সলিউশন খোঁজেন খুলে আপনি পদ্ধতি স্বাক্ষর পরিবর্তন করতে পারেন - পরিবর্তন Foo()যাতে এটি টাইপ ফেরৎ Taskএবং তারপর DoFoo()পারেন await Foo(), এই কোড হিসাবে:

public async Task Foo() {
    var x = await DoSomethingThatThrows();
}

public async void DoFoo() {
    try {
        await Foo();
    } catch (ProtocolException ex) {
        // This will catch exceptions from DoSomethingThatThrows
    }
}

19
এটি সত্যিই আপনার দিকে ঝুঁকতে পারে এবং সংকলক দ্বারা সতর্ক করা উচিত।
GGleGrand

19

আপনার কোড আপনাকে যা ভাবেন তা করতে পারে না। অ্যাসিঙ্ক পদ্ধতিগুলি অ্যাসিঙ্ক ফলাফলের জন্য অপেক্ষা করা শুরু করার সাথে সাথেই ফিরে আসে। কোডটি আসলে কীভাবে আচরণ করছে তা খতিয়ে দেখতে ট্রেসিং ব্যবহার করা অন্তর্দৃষ্টিপূর্ণ।

নীচের কোডটি নিম্নলিখিতটি করে:

  • 4 টি কার্য তৈরি করুন
  • প্রতিটি টাস্ক অবিচ্ছিন্নভাবে একটি সংখ্যা বাড়িয়ে তুলবে এবং বর্ধিত সংখ্যাটি ফিরিয়ে দেবে
  • অ্যাসিঙ্ক ফলাফল এলে এটি সনাক্ত করা হয়।

 

static TypeHashes _type = new TypeHashes(typeof(Program));        
private void Run()
{
    TracerConfig.Reset("debugoutput");

    using (Tracer t = new Tracer(_type, "Run"))
    {
        for (int i = 0; i < 4; i++)
        {
            DoSomeThingAsync(i);
        }
    }
    Application.Run();  // Start window message pump to prevent termination
}


private async void DoSomeThingAsync(int i)
{
    using (Tracer t = new Tracer(_type, "DoSomeThingAsync"))
    {
        t.Info("Hi in DoSomething {0}",i);
        try
        {
            int result = await Calculate(i);
            t.Info("Got async result: {0}", result);
        }
        catch (ArgumentException ex)
        {
            t.Error("Got argument exception: {0}", ex);
        }
    }
}

Task<int> Calculate(int i)
{
    var t = new Task<int>(() =>
    {
        using (Tracer t2 = new Tracer(_type, "Calculate"))
        {
            if( i % 2 == 0 )
                throw new ArgumentException(String.Format("Even argument {0}", i));
            return i++;
        }
    });
    t.Start();
    return t;
}

আপনি ট্রেস পর্যবেক্ষণ যখন

22:25:12.649  02172/02820 {          AsyncTest.Program.Run 
22:25:12.656  02172/02820 {          AsyncTest.Program.DoSomeThingAsync     
22:25:12.657  02172/02820 Information AsyncTest.Program.DoSomeThingAsync Hi in DoSomething 0    
22:25:12.658  02172/05220 {          AsyncTest.Program.Calculate    
22:25:12.659  02172/02820 {          AsyncTest.Program.DoSomeThingAsync     
22:25:12.659  02172/02820 Information AsyncTest.Program.DoSomeThingAsync Hi in DoSomething 1    
22:25:12.660  02172/02756 {          AsyncTest.Program.Calculate    
22:25:12.662  02172/02820 {          AsyncTest.Program.DoSomeThingAsync     
22:25:12.662  02172/02820 Information AsyncTest.Program.DoSomeThingAsync Hi in DoSomething 2    
22:25:12.662  02172/02820 {          AsyncTest.Program.DoSomeThingAsync     
22:25:12.662  02172/02820 Information AsyncTest.Program.DoSomeThingAsync Hi in DoSomething 3    
22:25:12.664  02172/02756          } AsyncTest.Program.Calculate Duration 4ms   
22:25:12.666  02172/02820          } AsyncTest.Program.Run Duration 17ms  ---- Run has completed. The async methods are now scheduled on different threads. 
22:25:12.667  02172/02756 Information AsyncTest.Program.DoSomeThingAsync Got async result: 1    
22:25:12.667  02172/02756          } AsyncTest.Program.DoSomeThingAsync Duration 8ms    
22:25:12.667  02172/02756 {          AsyncTest.Program.Calculate    
22:25:12.665  02172/05220 Exception   AsyncTest.Program.Calculate Exception thrown: System.ArgumentException: Even argument 0   
   at AsyncTest.Program.c__DisplayClassf.Calculateb__e() in C:\Source\AsyncTest\AsyncTest\Program.cs:line 124   
   at System.Threading.Tasks.Task`1.InvokeFuture(Object futureAsObj)    
   at System.Threading.Tasks.Task.InnerInvoke()     
   at System.Threading.Tasks.Task.Execute()     
22:25:12.668  02172/02756 Exception   AsyncTest.Program.Calculate Exception thrown: System.ArgumentException: Even argument 2   
   at AsyncTest.Program.c__DisplayClassf.Calculateb__e() in C:\Source\AsyncTest\AsyncTest\Program.cs:line 124   
   at System.Threading.Tasks.Task`1.InvokeFuture(Object futureAsObj)    
   at System.Threading.Tasks.Task.InnerInvoke()     
   at System.Threading.Tasks.Task.Execute()     
22:25:12.724  02172/05220          } AsyncTest.Program.Calculate Duration 66ms      
22:25:12.724  02172/02756          } AsyncTest.Program.Calculate Duration 57ms      
22:25:12.725  02172/05220 Error       AsyncTest.Program.DoSomeThingAsync Got argument exception: System.ArgumentException: Even argument 0  

Server stack trace:     
   at AsyncTest.Program.c__DisplayClassf.Calculateb__e() in C:\Source\AsyncTest\AsyncTest\Program.cs:line 124   
   at System.Threading.Tasks.Task`1.InvokeFuture(Object futureAsObj)    
   at System.Threading.Tasks.Task.InnerInvoke()     
   at System.Threading.Tasks.Task.Execute()     

Exception rethrown at [0]:      
   at System.Runtime.CompilerServices.TaskAwaiter.EndAwait()    
   at System.Runtime.CompilerServices.TaskAwaiter`1.EndAwait()  
   at AsyncTest.Program.DoSomeThingAsyncd__8.MoveNext() in C:\Source\AsyncTest\AsyncTest\Program.cs:line 106    
22:25:12.725  02172/02756 Error       AsyncTest.Program.DoSomeThingAsync Got argument exception: System.ArgumentException: Even argument 2  

Server stack trace:     
   at AsyncTest.Program.c__DisplayClassf.Calculateb__e() in C:\Source\AsyncTest\AsyncTest\Program.cs:line 124   
   at System.Threading.Tasks.Task`1.InvokeFuture(Object futureAsObj)    
   at System.Threading.Tasks.Task.InnerInvoke()     
   at System.Threading.Tasks.Task.Execute()     

Exception rethrown at [0]:      
   at System.Runtime.CompilerServices.TaskAwaiter.EndAwait()    
   at System.Runtime.CompilerServices.TaskAwaiter`1.EndAwait()  
   at AsyncTest.Program.DoSomeThingAsyncd__8.MoveNext() in C:\Source\AsyncTest\AsyncTest\Program.cs:line 0      
22:25:12.726  02172/05220          } AsyncTest.Program.DoSomeThingAsync Duration 70ms   
22:25:12.726  02172/02756          } AsyncTest.Program.DoSomeThingAsync Duration 64ms   
22:25:12.726  02172/05220 {          AsyncTest.Program.Calculate    
22:25:12.726  02172/05220          } AsyncTest.Program.Calculate Duration 0ms   
22:25:12.726  02172/05220 Information AsyncTest.Program.DoSomeThingAsync Got async result: 3    
22:25:12.726  02172/05220          } AsyncTest.Program.DoSomeThingAsync Duration 64ms   

আপনি লক্ষ্য করবেন যে রান পদ্ধতিটি 2820 থ্রেডে সম্পূর্ণ হয়েছে যখন কেবলমাত্র একটি সন্তানের থ্রেড শেষ হয়েছে (2756)। আপনি যদি আপনার প্রতীক্ষিত পদ্ধতির আশেপাশে একটি চেষ্টা / ক্যাপচার রাখেন তবে আপনি ব্যতিক্রমটিকে স্বাভাবিক উপায়ে "ধরতে" পারেন যদিও আপনার কোডটি অন্য থ্রেডে কার্যকর করা হয় যখন গণনার টাস্কটি শেষ হয়ে যায় এবং আপনার ধারণাটি কার্যকর করা হয় uted

গণনা পদ্ধতিটি নিক্ষিপ্ত ব্যতিক্রমগুলি স্বয়ংক্রিয়ভাবে ট্রেস করে কারণ আমি অ্যাপি চ্যাঞ্জ থেকে ApiChange.Api.dll ব্যবহার করেছি because সরঞ্জামটি ব্যবহার করেছি। ট্র্যাকিং এবং রিফ্লেক্টর যা চলছে তা বুঝতে অনেক সাহায্য করে। থ্রেডিং থেকে পরিত্রাণ পেতে আপনি নিজের গেটএইটার বিগেইনওয়েট এবং এন্ডএইয়েটের নিজস্ব সংস্করণ তৈরি করতে পারেন এবং কোনও টাস্ক নয় তবে উদাহরণস্বরূপ আপনার নিজস্ব এক্সটেনশন পদ্ধতির মধ্যে একটি অলস এবং ট্রেস তৈরি করতে পারেন। তারপরে আপনি আরও ভালভাবে বুঝতে পারবেন যে সংকলকটি কি করে এবং টিপিএল কী করে।

এখন আপনি দেখতে পাচ্ছেন যে আপনার ব্যতিক্রমটিকে ফিরিয়ে আনার / চেষ্টা করার কোনও উপায় নেই কারণ প্রচারের কোনও ব্যতিক্রমের জন্য কোনও স্ট্যাক ফ্রেম নেই। আপনি async ক্রিয়াকলাপ শুরু করার পরে আপনার কোড সম্পূর্ণ ভিন্ন কিছু করছে। এটি থ্রেড কল করতে পারে leep ঘুম বা এমনকি সমাপ্ত। যতক্ষণ না কোনও পূর্বভূমি থ্রেড থাকে ততক্ষণ আপনার অ্যাপ্লিকেশন আনন্দের সাথে অ্যাসিক্রোনাস কাজগুলি চালিয়ে যেতে থাকবে।


আপনার অ্যাসিনক্রোনাস অপারেশন শেষ হয়ে গেলে এবং ইউআই থ্রেডে ফিরে কল করার পরে আপনি অ্যাসিঙ্ক পদ্ধতির অভ্যন্তরে ব্যতিক্রমটি পরিচালনা করতে পারেন। এটি করার প্রস্তাবিত উপায়টি হল TaskScheduler.FromSynchronizationContext এর সাথে । এটি কেবল তখনই কাজ করে যদি আপনার কাছে একটি ইউআই থ্রেড থাকে এবং এটি অন্যান্য জিনিসে খুব বেশি ব্যস্ত না হয়।


5

ব্যতিক্রমটি async ফাংশনে ধরা পড়তে পারে।

public async void Foo()
{
    try
    {
        var x = await DoSomethingAsync();
        /* Handle the result, but sometimes an exception might be thrown
           For example, DoSomethingAsync get's data from the network
           and the data is invalid... a ProtocolException might be thrown */
    }
    catch (ProtocolException ex)
    {
          /* The exception will be caught here */
    }
}

public void DoFoo()
{
    Foo();
}

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

সেক্ষেত্রে, callbacks ধারণা অনেক করা (ভাল পুরানো ASYNC প্রতিনিধিদের)।
Sanjeevakumar Hiremath

@ টিম: নিক্ষিপ্ত ব্যতিক্রমে আপনার যা তথ্য প্রয়োজন তা অন্তর্ভুক্ত করবেন?
এরিক জে।

5

এটি উল্লেখ করাও গুরুত্বপূর্ণ যে আপনি যদি অ্যাসিঙ্ক পদ্ধতিতে আপনার শূন্য রিটার্ন টাইপ করেন তবে আপনি ব্যতিক্রমের কালানুক্রমিক স্ট্যাক ট্রেসটি হারাবেন। আমি নিম্নলিখিত হিসাবে টাস্ক ফিরে সুপারিশ করব। ডিবাগিং পুরোপুরি সহজ করতে চলেছে।

public async Task DoFoo()
    {
        try
        {
            return await Foo();
        }
        catch (ProtocolException ex)
        {
            /* Exception with chronological stack trace */     
        }
    }

সমস্ত পাথ একটি মান ফেরত না দেওয়ার কারণে এটি একটি সমস্যা তৈরি করবে, কারণ যদি ব্যতিক্রম হয় তবে কোনও মান ফেরত পাওয়া যায় না, চেষ্টা করার সময় রয়েছে। যদি আপনার কোনও returnবক্তব্য না থাকে তবে এই কোডটি কাজ করে, যেহেতু Task"স্পষ্টতই" ব্যবহার করে ফিরে এসেছে async / await
মাতিয়াস গ্রিওনি

2

এই ব্লগটি আপনার সমস্যার সুস্পষ্টভাবে অ্যাসিঙ্ক সেরা অভ্যাসগুলি ব্যাখ্যা করে ।

এটির সংক্ষিপ্তসার হিসাবে আপনি একটি অ্যাসিঙ্ক পদ্ধতির বিনিময়ে শূন্যতা ব্যবহার করা উচিত নয়, যদি না এটি কোনও অ্যাসিঙ্ক ইভেন্ট হ্যান্ডলার থাকে তবে এটি খারাপ অভ্যাস কারণ এটি ব্যতিক্রমগুলি ধরা দেয় না ;-)।

সেরা অনুশীলন হ'ল রিটার্নের ধরনটি টাস্কে পরিবর্তন করা। এছাড়াও, অ্যাসিঙ্ককে সমস্ত পথে ট্রাকে কোড দেওয়ার চেষ্টা করুন, প্রতিটি অ্যাসিঙ্ক পদ্ধতি কল করুন এবং এসিঙ্ক পদ্ধতি থেকে কল করুন। কনসোলে একটি মূল পদ্ধতি ব্যতীত, যা অ্যাসিঙ্ক হতে পারে না (সি # 7.1 এর আগে)।

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

এই আচরণটি কোনও কনসোল অ্যাপ্লিকেশনটিতে ঘটবে না, কারণ এটি থ্রেড পুলের সাথে প্রসঙ্গে চলে। অ্যাসিঙ্ক পদ্ধতিটি অন্য থ্রেডে ফিরে আসবে যা নির্ধারিত হবে। এই কারণেই একটি পরীক্ষা কনসোল অ্যাপ্লিকেশনটি কাজ করবে, তবে একই কলগুলি অন্যান্য অ্যাপ্লিকেশনগুলিতে ডেডলক করবে ...


1
"কনসোলে একটি মূল পদ্ধতি ব্যতীত, যা অ্যাসিঙ্ক হতে পারে না" " সি # 7.1 যেহেতু, মেইন এখন অ্যাসিঙ্ক পদ্ধতির লিঙ্ক
অ্যাডাম
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.