1

Я пытаюсь выполнить итерацию через цикл, нажав кнопку запуска и остановив ее, нажав кнопку остановки. Я использую await Task.Run(() =>, он работает в ожидании. Но когда я снова нажимаю кнопку запуска, я получаю TargetInvokationException в Application.Run(new Form1());.TargetInvokationException в Application.Run (новый Form1());

Мой код ниже

using System.Threading; 
using System.Threading.Tasks; 
using System.Windows.Forms; 

namespace CancellationTest 
{ 
    public partial class Form1 : Form 
    { 
     private readonly SynchronizationContext synchronizationContext; 
     private DateTime previousTime = DateTime.Now; 

     CancellationTokenSource cts = new CancellationTokenSource(); 

     public Form1() 
     { 
      InitializeComponent(); 
      synchronizationContext = SynchronizationContext.Current; 
     } 

     private async void ButtonClickHandlerAsync(object sender, EventArgs e) 
     { 
      button1.Enabled = false; 
      var count = 0; 

      CancellationToken token = cts.Token; 

      await Task.Run(() => 
      { 
       try 
       { 
        for (var i = 0; i <= 5000000; i++) 
        { 
         token.ThrowIfCancellationRequested(); 

         UpdateUI(i); 
         count = i; 
        } 
       } 
       catch (System.OperationCanceledException) 
       { 
        MessageBox.Show("Canceled"); 
       } 
      }, token); 

      label1.Text = @"Counter " + count; 
      button1.Enabled = true; 
     } 

     public void UpdateUI(int value) 
     { 
      var timeNow = DateTime.Now; 

      if ((DateTime.Now - previousTime).Milliseconds <= 50) return; 

      synchronizationContext.Post(new SendOrPostCallback(o => 
      { 
       label1.Text = @"Counter " + (int)o; 
      }), value); 

      previousTime = timeNow; 
     } 

     private void button2_Click(object sender, EventArgs e) 
     { 
      cts.Cancel(); 
     } 
    } 
} 

Может кто-нибудь объяснить, почему это происходит и как решить эту проблему.

+0

Исправьте необработанную диагностику исключения, что бы это ни потребовалось. С такими исключениями вы всегда ** хотите знать внутреннее исключение. Нажмите ссылку «Подробнее» в диалоговом окне «Ассистент исключения». Довольно легко увидеть, что CancellationToken устарел. –

ответ

1

Может кто-нибудь объяснить, почему это происходит

TargetInvokationException является исключением типа обертки и основная информация содержится в InnerException имущества, которое вы не показали. Глядя на код, скорее всего это OperationCanceledException и вызван передачей уже отмененного токена на Task.Run.

В общем, вы не должны повторно использовать экземпляр CancellationTokenSource - см общую схему для реализации кооперативной модели отмены в Замечания раздела документации.

Также вам следует защитить весь блок, а не только корпус Task.Run. И сначала инициализируйте всех задействованных членов государства, и сделайте необходимую очистку в конце.

Так правильный код может выглядеть так:

private DateTime previousTime; 
private CancellationTokenSource cts; 

private async void ButtonClickHandlerAsync(object sender, EventArgs e) 
{ 
    button1.Enabled = false; 
    var count = 0; 
    previousTime = DateTime.Now; 
    cts = new CancellationTokenSource(); 
    try 
    { 
     CancellationToken token = cts.Token; 
     await Task.Run(() => 
     { 
      for (var i = 0; i <= 5000000; i++) 
      { 
       token.ThrowIfCancellationRequested(); 
       UpdateUI(i); 
       count = i; 
      } 
     }, token); 
    } 
    catch (System.OperationCanceledException) 
    { 
     MessageBox.Show("Canceled"); 
    } 
    finally 
    { 
     cts.Dispose(); 
     cts = null; 
    } 
    label1.Text = @"Counter " + count; 
    button1.Enabled = true; 
} 

и убедитесь, что Cancel кнопка включена только тогда, когда cts != null или проверить, что условие внутри обработчика щелчка, чтобы избежать NRE.

+0

Это нормально для 'catch (System.OperationCanceledException)'. Мое личное чувство заключается в том, что это можно сделать надлежащим образом. Есть идеи? – user2129013

+1

Почему бы и нет - это исключение, как и любое другое. Если у вас есть вложенные задачи, вы не должны улавливать их, чтобы обеспечить надлежащую отмену задачи, но здесь вы находитесь в вызове верхнего уровня ('async void'), что означает, что никакой код не может перехватить ваши исключения. На самом деле вы должны поймать все исключения в обработчиках событий, как указано выше, иначе ваше приложение будет разбиваться. –

 Смежные вопросы

  • Нет связанных вопросов^_^