2016-05-31 21 views
5

CancellationToken.IsCancelRequested algılandığında, kitaplıklarımın OperationCancelledException'dan başka bir şeye attığından yöntemlerden çıkmasını sağlamak kötü bir uygulamadır mı? ÖrneğinCancellationToken ayarlandığında keyfi bir istisna atmak kötü bir uygulama mı?

:

async Task<TcpClient> ConnectAsync(string host, int port, CancellationToken ct) 
{ 
    var client = new TcpClient(); 
    try 
    { 
     using (ct.Register(client.Close, true)) 
     { 
      await client.ConnectAsync(host, port); 
     } 

     // Pick up strugglers here because ct.Register() may have hosed our client 
     ct.ThrowIfCancellationRequested(); 
    } 
    catch (Exception) 
    { 
     client.Close(); 
     throw; 
    } 

    return client; 
} 

iptal edildikten sonra, bu işlem ile ilgili olarak ObjectDisposedException veya NullReferenceException atma olasılığı vardır. (TcpClient.ConnectAsync() atabilir Çünkü ya bir eşzamanlı çağrıldığında TcpClient.Close().)

Şimdi, şöyle çözeriz diye:

async Task<TcpClient> ConnectAsync(string host, int port, CancellationToken ct) 
{ 
    var client = new TcpClient(); 
    try 
    { 
     using (ct.Register(client.Close, true)) 
     { 
      try 
      { 
       await client.ConnectAsync(host, port); 
      } 
      catch (Exception) 
      { 
       // These exceptions are likely because we closed the 
       // connection with ct.Register(). Convert them to 
       // OperationCancelledException if that's the case 
       ct.ThrowIfCancellationRequested(); 
       throw; 
      } 
     } 

     // Pick up strugglers here because ct.Register() may have hosed our client 
     ct.ThrowIfCancellationRequested(); 
    } 
    catch (Exception) 
    { 
     client.Close(); 
     throw; 
    } 

    return client; 
} 

Ve aynı şekilde uygulanabilir çağrı hiyerarşisinin her katmanında:

async Task<TcpClient> ConnectSslStreamAsync(string host, int port, CancellationToken ct) 
{ 
    var client = await ConnectAsync(host, port, ct); 
    try 
    { 
     ct.ThrowIfCancellationRequested(); 

     var sslStream = new SslStream(client.getStream()); 
     using (ct.Register(sslStream.Close)) 
     { 
      try 
      { 
       await sslStream.AuthenticateAsClientAsync(...); 
      } 
      catch (Exception) 
      { 
       // These exceptions are likely because we closed the 
       // stream with ct.Register(). Convert them to 
       // OperationCancelledException if that's the case 
       ct.ThrowIfCancellationRequested(); 
       throw; 
      } 
     } 

     // Pick up strugglers here because ct.Register() may have hosed our stream 
     ct.ThrowIfCancellationRequested(); 
    } 
    catch (Exception) 
    { 
     client.Close(); 
     throw; 
    } 

    return client; 
} 

Ancak bu, gerçekte tüm gereken, dış katmanda bir kez kontrol edildiğinde, bu ekstra kodu ekler. (İptal kaynağında.)

Bu keyfi istisnaların uçmasına izin vermek kötü bir uygulama mı? Yoksa onları en çok arayan kişiyi görmezden mi geliyorlar?

+1

Microsoft, bir istisna atar bir yöntem açıkça eklediğinde, hayır, "kötü uygulama" akla ilk gelen şey değil. Sadece atılmadığını iddia etmek kötü bir uygulamadır. –

+1

"ConnectAsync" in sonuna "ct.ThrowIfCancellationRequested()" koyamıyorum bile. Bu görüşmeden sonra ancak ConnectAsync'in geri dönmesinden önce jeton iptal edilirse ne olur? Kitaplık istemcisinin çeşitli yarış istisnalarıyla uğraşmasına izin vermeyi tercih ediyorum ve yalnızca olası bir yan etkiyi belgeleyecektim. – Noseratio

+0

@Noseratio Bu 'ct.ThrowIfCancellationRequested()' var, çünkü o olmadan, '(ct.Register (...)) '' 'ConnectAsync()'' in devam ettiği açık bağlantıyı kapatabildiği ince bir yarış var. başarıya. Örneğimizde bir fark yaratamayabilir (ve arayıcı, bağlantıyı her halükarda tasfiye edecektir), fakat bu yapının, yöntemlerin uygulandığı durumlarda bütünlüğü (atomisite) korumak için muhtemelen bir kural olarak saklı tutulmaya değerdir. durumları değiştir. – antak

cevap

2

İptal gerekiyorsa OperationCancelledException'u atmanız gerekir. Kodunuzun tüketicileri istisna aldığında, aslında bir iptal ya da başka bir şey olsaydı hiçbir fikre sahip olmayacaklar.

, size TcpClient veya akışı kapatılması için bir görev oluşturmak ve ilk tamamladı hangi görevi doğrulayıp harekete geçecektir nerede yaklaşım here sağlanan deneyebilirsiniz nedeniyle yakın delegenin kaydetme OperationCancelledException atmak mümkün değilse, söz konusu bu buna göre.

+0

İkinci paragrafınızla ne demek istediğinden emin değilim.Bir "OperationCancelledException" işlevini zaman aşımı amacıyla ikinci bir "CancellationTokenSource" kullandığında, bir "OperationCancelledException" işlevini bir "TimeoutException" olarak değiştirmeyi anlatırsınız ve bu, istisnanın 'CancellationToken' alanını (kabul edilen yanıtta gösterildiği gibi) karşılaştırarak mümkündür). Buradaki soru, bir istisna X'in bir "OperationCancelledException" olarak değiştirilmesi ile ilgilidir, ancak bu sadece 'catching' (Exception) {ct.ThrowIfCancellationRequested; atmak; } 'yukarıda gösterildiği gibi. – antak

İlgili konular