2012-01-31 14 views
7

Tengo un problema para saber cómo cancelar esta tarea en C#. No entiendo muy bien el manejo de los hilos y he intentado buscar en Google algunos ejemplos de código simples para ayudarme, pero realmente no he llegado a ninguna parte. Este es el código en el que estoy trabajando:Cancelando una tarea que recupera URLs asincrónicamente

var tasks = urls.Select(url => Task.Factory.StartNew(
state => 
{ 
    using (var client = new WebClient()) 
    { 

     lock (this) 
     { 

     // code to download stuff from URL 

     } 

    } 
}, url)).ToArray(); 

    try 
    { 
     Task.WaitAll(tasks); 
    } 
    catch (Exception e) 
    { 
     textBox2.AppendText("Error: " + e.ToString()); 
    } 

Donde "urls" es una matriz de URL. ¿Existe una manera simple de hacerlo de modo que, cuando hago clic en un botón en mi programa, la descarga de las URL se detenga por completo? Además, el fragmento de código que pegué está en una función que backgroundWorker1 llama, lo que supongo que puede complicar un poco más las cosas. (La razón por la que tengo un backgroundWorker es para que la UI no se bloquee mientras está descargando URL.)

Si de alguna manera es un poco confuso, aquí hay un resumen de lo que estaba tratando de lograr con mi código:

  • Tengo una serie de URL, me gustaría descargar todas las URL de forma asincrónica sin bloquear la interfaz de usuario.
  • Preferiría que el usuario detenga el programa para descargar URL haciendo clic en un botón, casi cancelando el hilo.
  • Cuando el usuario vuelve a hacer clic en el botón, el programa vuelve a descargar las URL de esa matriz.

Gracias de antemano.

+0

No incluya etiquetas en el título de su pregunta; simplemente hace más trabajo para aquellos de nosotros que se preocupan por la consistencia en SO. –

Respuesta

1

No sé si esta es la forma correcta de hacerlo o no, pero he podido cancelar tareas usando el siguiente código. Creé un formulario con ListBox y ProgressBar, así que estoy criando y manejando el evento ProgressChanged de BackgroundWorker. Espero que esto te ayude de alguna manera.

void bw_DoWork(object sender, DoWorkEventArgs e) 
{ 
    CancellationTokenSource _tokenSource = new CancellationTokenSource(); 
    CancellationToken _token = _tokenSource.Token; 

    var urls = e.Argument as IEnumerable<string>; 

    _token = new CancellationToken(); 

    if (urls == null) return; 

    var i = 0; 
    var a = 100/urls.Count(); 

    var sb = new StringBuilder(); 
    var t = urls.Select(url => Task.Factory.StartNew(
        (u) =>{ 
         using (var wc = new WebClient()) 
         { 
          lock (this){ 
           var s = wc.DownloadString(u.ToString()); 
           sb.AppendFormat("{1}:{0}\r\n", "", u); 
          } 
         } 

        if (Worker.CancellationPending){ 
         //MAGIC HAPPENS HERE, IF BackgroundWorker IS REQUESTED 
         //TO CANCEL, WE CANCEL CancellationTokenSource 
         _tokenSource.Cancel(); 
        } 

        //IF CANCELATION REQUESTED VIA CancellationTokenSource 
        //THROW EXCEPTION WHICH WILL ADD TO AggreegateException 
        _token.ThrowIfCancellationRequested(); 

        //YOU CAN IGNORE FOLLOWING 2 LINES 
        i += a; 
        Worker.ReportProgress(i, u); 
    }, url, _token)).ToArray(); 

    try 
    { 
     Task.WaitAll(t); 
    } 
    catch (AggregateException age) 
    { 
     if (age.InnerException is OperationCanceledException) 
      sb.Append("Task canceled"); 
    } 
    catch (Exception ex) 
    { 
     sb.Append(ex.Message); 
    } 

    e.Result = sb; 
} 
+0

Wow, ese ejemplo de código fue muy útil, creo que mi propia pieza de código funcionó, y el hilo se canceló con éxito. Aunque no quiero emocionarme demasiado, algo siempre podría surgir. :) – Chrispy

1

Con WebClient, puede utilizar el método CancelAsync para cancelar una operación asincrónica.

Para cancelar las tareas que está iniciando a través de Factory.StartNew, debe usar un CancellationTokenSource. Debe pasar CancellationTokenSource.Token a las tareas (y puede preguntar si el token ya se canceló usando token.IsCancellationRequested), y llamaría al CancellationTokenSource.Cancel() para configurar el token como cancelado.

+0

Saludos, me aseguraré de recordar esto en lo que respecta a Factory.StartNew – Chrispy

Cuestiones relacionadas