2015-02-20 24 views
13

CancellationTokenSource'umun Cancel() yöntemini çağırarak, async yöntemimi aşağıdaki içerikle iptal ettiğimde, sonunda duracaktır. Ancak, Console.WriteLine(await reader.ReadLineAsync()); hattının tamamlanması biraz uzun sürdüğü için, Cancel() çağrıma daha hızlı yanıt verebilmek için CancellationToken'i ReadLineAsync()'a (boş bir dizge döndürmesini bekleyerek) geçirmeyi denedim. Ancak CancellationToken'u ReadLineAsync()'a geçiremedim.StreamReader.ReadLineAsync'i CancellationToken ile iptal edebilir miyim?

Bir aramayı Console.WriteLine() veya Streamreader.ReadLineAsync() iptal edebilir miyim, eğer öyleyse, nasıl yapabilirim?

Neden ReadLineAsync()CancellationToken kabul edilmiyor? Yöntemin iptal edildikten sonra hala tamamlanmasına rağmen, Async yöntemlerine isteğe bağlı bir CancellationToken parametresi vermenin iyi bir uygulama olduğunu düşündüm.

StreamReader reader = new StreamReader(dataStream); 
while (!reader.EndOfStream) 
{ 
    if (ct.IsCancellationRequested){ 
     ct.ThrowIfCancellationRequested(); 
     break; 
    } 
    else 
    { 
     Console.WriteLine(await reader.ReadLineAsync()); 
    } 
} 

Güncelleme aşağıdaki yorum belirtilen gibi Console.WriteLine() çağrı yalnız zaten satıra 40.000 karakterlik bir kötü formated giriş dizesi nedeniyle birkaç saniye kadar alıyordu. Bunu aşmak yanıt-zaman meselelerini çözer, ancak bir sebepten dolayı 40.000 karakterin bir satırda yazılması istendiğinde (örneğin tüm dizeyi içine dökerken) bu uzun süreli ifadeyi nasıl iptal edeceğime dair herhangi bir öneri veya geçici çözümle ilgileniyorum. bir dosya).

+3

bakınız. Aşırı derecede kullanıcı-düşmanca bir program yazmışsınızdır, bu, kullanıcı tarafından okunandan çok daha yüksek bir hızda ekran boyunca cılızca kaydırma metni. Şimdi "bu saçmalıkları durdur" çözümünü arıyorsun. Tabii ki, sizin de kullanıcınız da öyle. Ama bu gerçek bir çözüm değil, doğru olanı asla başlamaz. Bir dosyaya yazın, Not Defteri ile görüntüleyin. Her şey daha iyi. –

+0

'Yanlış sorunu çözmeye çalışıyorsunuz.” Bu açık olduğunuz için teşekkür ederim - kesinlikle haklısınız. –

cevap

6

İptal edilemezse işlemi iptal edemezsiniz. Size

await task.WithCancellation(cancellationToken); 

Sen Console.WriteLine iptal edemez ve:

public static Task<T> WithCancellation<T>(this Task<T> task, CancellationToken cancellationToken) 
{ 
    return task.IsCompleted // fast-path optimization 
     ? task 
     : task.ContinueWith(
      completedTask => completedTask.GetAwaiter().GetResult(), 
      cancellationToken, 
      TaskContinuationOptions.ExecuteSynchronously, 
      TaskScheduler.Default); 
} 

Kullanımı: Kaçacak iptal edildi sanki kodunuzu akış davranıp için WithCancellation uzatma yöntemi kullanabilirsiniz, ama yine de olur yatan gerek yok. Makul bir boyutta string varsa anlıkdır.

Kılavuz hakkında: Uygulamanız aslında iptali desteklemiyorsa, karışık bir ileti gönderdiği için bir jetonu kabul etmemelisiniz.

Konsola yazmak için çok büyük bir diziniz varsa, Console.WriteLine'u kullanmamalısınız. Bir defada bir karakter dizesi yazmak ve yöntem iptal olması olabilir:

public void DumpHugeString(string line, CancellationToken token) 
{ 
    foreach (var character in line) 
    { 
     token.ThrowIfCancellationRequested(); 
     Console.Write(character); 
    } 

    Console.WriteLine(); 
} 

Çok daha iyi bir çözüm yerine tek karakterlerin gruplar halinde yazmak olacaktır.Yani sonuç olarak

public void DumpHugeString(string line, CancellationToken token) 
{ 
    foreach (var characterBatch in line.Batch(100)) 
    { 
     token.ThrowIfCancellationRequested(); 
     Console.Write(characterBatch.ToArray()); 
    } 

    Console.WriteLine(); 
} 

,:

public static async Task<T> WithCancellation<T>(this Task<T> task, CancellationToken cancellationToken, Action action, bool useSynchronizationContext = true) 
{ 
    using (cancellationToken.Register(action, useSynchronizationContext)) 
    { 
     try 
     { 
      return await task; 
     } 
     catch (Exception ex) 
     { 

      if (cancellationToken.IsCancellationRequested) 
      { 
       // the Exception will be available as Exception.InnerException 
       throw new OperationCanceledException(ex.Message, ex, cancellationToken); 
      } 

      // cancellation hasn't been requested, rethrow the original Exception 
      throw; 
     } 
    } 
} 

Artık iptal jetonu üzerinde kullanabilirsiniz:

var reader = new StreamReader(dataStream); 
while (!reader.EndOfStream) 
{ 
    DumpHugeString(await reader.ReadLineAsync().WithCancellation(token), token); 
} 
+0

@HW iptal işlemini desteklemiyorsa, Console.WriteLine işlevini iptal edemezsiniz, ancak 'Console.Write 'işlevini küçük harflerle kullanabilirsiniz. Güncellemeye bakın. – i3arnon

+0

'Task.ContinueWith' tüm taksileri de iptal edeceğinden emin misiniz? Benim bakış açımdan 'Task.ContinueWith' burada uygun değil. Bu örnekte olduğu gibi, Task.WhenAny (görev, infiniteCancellableTaks) 'türünde bir şeye ihtiyacınız vardır. Bu örnekte olduğu gibi, http://stackoverflow.com/a/23473779/2528649 – neleus

+0

@ neleus Evet. Orijinal görevi iptal etmez (çünkü yapamazsınız), ancak devam etmeyi iptal edecektir. Denemek için çekinmeyin. – i3arnon

0

Streamreader.ReadLineAsync() dosyasını iptal edemezsiniz. IMHO çünkü tek bir satır okumak çok hızlı olmalı. Ancak, Console.WriteLine()'un ayrı bir görev değişkeni kullanarak gerçekleşmesini kolayca önleyebilirsiniz.

ct.IsCancellationRequested denetimi de, iptal isteğinde bulunuyorsa ct.ThrowIfCancellationRequested() olarak atılacak.

StreamReader reader = new StreamReader(dataStream); 
while (!reader.EndOfStream) 
{ 
    ct.ThrowIfCancellationRequested(); 
    string line = await reader.ReadLineAsync()); 

    ct.ThrowIfCancellationRequested();  
    Console.WriteLine(line); 
} 
+0

'IsCancellationRequeded' için kontrol etmem gereken ipucu için teşekkür ederiz! Ne yazık ki iki görev arasındaki iptal için tekrar kontrol cevap zamanımı azalttı. –

+2

Tek bir çizgiyi okumak çok yavaş olabilir, örneğin bir stdout akışı veya bir ağ akışı okuyorsanız, diğer bir deyişle içeriğiniz bir çekimde tamamen materyalize edilmemiş bir akış olabilir. – dcstraw

1

Bunun için bu answer genelleştirilmiş İşte MoreLinq 'ın Batch kullanarak bir uygulama var herhangi bir canlanılabilir async metodu. örnek WebRequest.GetResponseAsync İçin:

var request = (HttpWebRequest)WebRequest.Create(url); 

using (var response = await request.GetResponseAsync()) 
{ 
    . . . 
} 

olacak:

var request = (HttpWebRequest)WebRequest.Create(url); 

using (WebResponse response = await request.GetResponseAsync().WithCancellation(CancellationToken.None, request.Abort, true)) 
{ 
    . . . 
} 

Yanlış sorunu çözmek için çalışıyoruz ömeği http://pastebin.com/KauKE0rW

+0

Örnekte bir yarış var. Yakalama işleyicisinde 'IsCancellationRequeded 'seçeneğini işaretlerseniz, ilk önce iptalin veya istisnanın gerçekleşip gerçekleşmediğini bilmezsiniz. Gerçekten bir iptal özel durumu olup olmadığını görmek için özel durum türünü denetlemelisiniz. – Kenneth

İlgili konular