2012-09-07 25 views
18

Beklenen bir async yöntemi, bir istisna atarsa, istisna bir yerde saklanır ve atma geciktirilir. Bir WinForms veya WPF uygulamasında, istisnayı fırlatmak için SynchronizationContext.Current kullanır. Bununla birlikte, örn. Bir konsol uygulaması, bir iş parçacığı havuzu üzerinde istisna atar ve uygulamayı indirir.Eşzamansız istisnaları async'den yakala

async yönteminden atılan istisnaların uygulamayı aşağıya çekmesini nasıl önleyebilirim?

DÜZENLEME: voidasync yöntemleri var çünkü

Appearantly ben tarif ediyorum konudur. Yorumlara bakınız.

+2

Bir uyumsuzluk yöntemi bekleniyorsa, istisna onu bekleyen kod içine atılır. İşlenmeyen istisnalar, bu yöntem yalnızca "void" dönüyorsa davranır. Bu, "async void" yöntemlerini mümkün olduğunca kullanmamak için bir neden. – svick

+1

@svick 'async void' yöntemleri deterministik davranışla sonuçlanır; Bir özel durumla sonuçlanan ve görevi beklemeyen bir Görev işlevi döndürme işlevi, çöp toplayıcının çalıştığı gelecekte gelecekte bazı yarıda noktalarında "UnobservedTaskException" olayını yükseltir ve bu hiçbir şey yapmazsa, programın sessizce devam etmesini sağlar. eğer her şey yolundaysa. Sorun “async void” yöntemleriyle değil, sadece gerçek problemi ortaya çıkarmaktadır. Bir "async" yönteminden bir 'Task' döndüren işlevi çağırmazsanız, yanlış bir şey yaptığınız için iyi bir şans vardır. – hvd

+0

Bu arada, sadece "iyi şans" çünkü istisnalar atmak için uygun olmayan birkaç istisna (cezaya çarptırılmadı) vakası var, ama çok fazla değil. – hvd

cevap

10

async yöntemi başlatıldığında, geçerli eşitleme içeriğini yakalar. Bu sorunu çözmenin bir yolu, istisnayı yakalayan kendi senkronizasyon bağlamınızı oluşturmaktır. Yukarıdaki catch yılında

public class AsyncSynchronizationContext : SynchronizationContext 
{ 
    public override void Send(SendOrPostCallback d, object state) 
    { 
     try 
     { 
      d(state); 
     } 
     catch (Exception ex) 
     { 
      // Put your exception handling logic here. 

      Console.WriteLine(ex.Message); 
     } 
    } 

    public override void Post(SendOrPostCallback d, object state) 
    { 
     try 
     { 
      d(state); 
     } 
     catch (Exception ex) 
     { 
      // Put your exception handling logic here. 

      Console.WriteLine(ex.Message); 
     } 
    } 
} 

sizin istisna işleme mantığı koyabilirsiniz: Burada

nokta senkronizasyon bağlam mesajlar parçacığı havuzuna geri arama, ama bunun geçici bir try/catch ile olmasıdır.

Daha sonra, bu mekanizma ile async yöntemlerini yürütmek istediğiniz her iş parçacığı (SynchronizationContext.Current[ThreadStatic] Aslında), geçerli eşitleme bağlamını ayarlamak gerekir:

SynchronizationContext.SetSynchronizationContext(new AsyncSynchronizationContext()); 

tam Main örnek:

class Program 
{ 
    static void Main(string[] args) 
    { 
     SynchronizationContext.SetSynchronizationContext(new AsyncSynchronizationContext()); 

     ExecuteAsyncMethod(); 

     Console.ReadKey(); 
    } 

    private static async void ExecuteAsyncMethod() 
    { 
     await AsyncMethod(); 
    } 

    private static async Task AsyncMethod() 
    { 
     throw new Exception("Exception from async"); 
    } 
} 
+3

Üzgünüm ama SynchronizationContext'in iş parçacığı havuzunda herhangi bir şeyi nereye yazdığını tam olarak göremiyorum - tüm bunları görebildiğim kadarıyla, çağrı iş parçacığı üzerinde hemen işlevi çalıştırır ve geri döner. –

18

Bir uyumsuzluk yönteminden atılan istisnaların uygulamayı aşağı getirmesini nasıl önleyebilirim?

bu en iyi uygulamaları takip edin: bunlar void (örneğin olay işleyicileri) dönmek için sahip sürece

  1. Tüm async yöntemleri Task veya Task<T> dönmelidir.
  2. Bir noktada, tüm Task s async yöntemlerinden döndürülmelidir. Bunu yapmak istememenizin tek nedeni, işlemin sonucunu (örneğin, iptal ettikten sonra) umursamıyorsanız.
  3. Bir async void olay işleyicisinden bir istisna yakalamanız gerekiyorsa, olay işleyicisinde yakalayın - bu eşzamanlı bir kod olsaydı yaptığınız gibi.

Benim async/await intro post'um yararlı bulabilirsiniz; Orada başka birçok iyi uygulamaları da ele alıyorum.

+1

Anladığım problemin, "async" (geçersiz) yöntemlere sahip olduğumu anlıyorum. Ancak, bu, tanımladığım sorunun olay işleyicileri için gerçekleştiği anlamına mı geliyor? –

+0

Evet; Üçüncü noktayı yinelemek için, bir "async void" olay işleyiciniz varsa, muhtemelen bu olay işleyicisi içinde * istisnaları yakalamak istersiniz. Eşzamanlı bir olay işleyicisi aynı soruna sahip olacak - bir iş parçacığı havuzu iş parçacığı üzerinde çalışırken bir özel durum kaçarsa uygulamayı kilitler. –

+0

İstisnalar, "async" yöntemi çağrıldığında yakalanan 'SynchronizationContext''e yönlendirilir. Bir WinForms/WPF uygulamasında, çoğu zaman UI iş parçacığı tarafından büyük olasılıkla başlatıldığından bu doğru bir eşitleme bağlamı olacaktır. Davranış, olayın "eşzamansız" olmadığı zaman gerçekleşenlerden farklı olmadığı anlamına gelir. –