Отменить асинхронную задачу с помощью кнопки

Что мне нужно сделать, так это иметь возможность отменить задачу, которая выполняется асинхронно.

Я искал и, кажется, не могу обернуть вокруг себя. Я просто не могу понять, как это будет реализовано в моей текущей настройке.

Вот мой код, который запускает мою задачу. Любая помощь в том, где и как реализовать токен отмены, будет принята с благодарностью.

    private async void startThread()
    {
        //do ui stuff before starting
        ProgressLabel.Text = String.Format("0 / {0} Runs Completed", index.Count());
        ProgressBar.Maximum = index.Count();

        await ExecuteProcesses();

        //sort list of output lines
        outputList = outputList.OrderBy(o => o.RunNumber).ToList();

        foreach (Output o in outputList)
        {
            string outStr = o.RunNumber + "," + o.Index;
            foreach (double oV in o.Values)
            {
                outStr += String.Format(",{0}", oV);
            }

            outputStrings.Add(outStr);
        }

        string[] csvOut = outputStrings.ToArray();

        File.WriteAllLines(settings.OutputFile, csvOut);
        //do ui stuff after completing.

        ProgressLabel.Text = index.Count() + " runs completed. Output written to file test.csv";
    }

    private async Task ExecuteProcesses()
    {
        await Task.Factory.StartNew(() =>
        {
            int myCount = 0;
            int maxRuns = index.Count();
            List<string> myStrings = index;
            Parallel.ForEach(myStrings,
                new ParallelOptions()
                {
                    MaxDegreeOfParallelism = settings.ConcurrentRuns
                }, (s) =>
                {
                    //This line gives us our run count.
                    int myIndex = myStrings.IndexOf(s) + 1;

                    string newInputFile = Path.Combine(settings.ProjectPath + "files/", Path.GetFileNameWithoutExtension(settings.InputFile) + "." + s + ".inp");
                    string newRptFile = Path.Combine(settings.ProjectPath + "files/", Path.GetFileNameWithoutExtension(settings.InputFile) + "." + s + ".rpt");

                    try
                    {
                        //load in contents of input file
                        string[] allLines = File.ReadAllLines(Path.Combine(settings.ProjectPath, settings.InputFile));


                        string[] indexSplit = s.Split('.');

                        //change parameters here
                        int count = 0;
                        foreach (OptiFile oF in Files)
                        {
                            int i = Int32.Parse(indexSplit[count]);
                            foreach (OptiParam oP in oF.Parameters)
                            {
                                string line = allLines[oP.LineNum - 1];
                                if (oP.DecimalPts == 0)
                                {
                                    string sExpression = oP.Value;
                                    sExpression = sExpression.Replace("%i", i.ToString());
                                    EqCompiler oCompiler = new EqCompiler(sExpression, true);
                                    oCompiler.Compile();
                                    int iValue = (int)oCompiler.Calculate();

                                    allLines[oP.LineNum - 1] = line.Substring(0, oP.ColumnNum - 1) + iValue.ToString() + line.Substring(oP.ColumnNum + oP.Length);
                                }
                                else
                                {
                                    string sExpression = oP.Value;
                                    sExpression = sExpression.Replace("%i", i.ToString());
                                    EqCompiler oCompiler = new EqCompiler(sExpression, true);
                                    oCompiler.Compile();
                                    double dValue = oCompiler.Calculate();
                                    dValue = Math.Round(dValue, oP.DecimalPts);

                                    allLines[oP.LineNum - 1] = line.Substring(0, oP.ColumnNum - 1) + dValue.ToString() + line.Substring(oP.ColumnNum + oP.Length);
                                }
                            }
                            count++;
                        }
                        //write new input file here
                        File.WriteAllLines(newInputFile, allLines);
                    }
                    catch (IOException ex)
                    {
                        MessageBox.Show(ex.ToString());
                    }


                    var process = new Process();
                    process.StartInfo = new ProcessStartInfo("swmm5.exe", newInputFile + " " + newRptFile);
                    process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
                    process.Start();
                    process.WaitForExit();

                    Output output = new Output();
                    output.RunNumber = myIndex;
                    output.Index = s;
                    output.Values = new List<double>();

                    foreach(OutputValue oV in OutputValues) {
                         output.Values.Add(oV.getValue(newRptFile));
                    }

                    outputList.Add(output);

                    //get rid of files after run
                    File.Delete(newInputFile);
                    File.Delete(newRptFile);

                    myCount++;
                    ProgressBar.BeginInvoke(
                        new Action(() =>
                            {
                                ProgressBar.Value = myCount;
                            }
                    ));
                    ProgressLabel.BeginInvoke(
                        new Action(() =>
                            {
                                ProgressLabel.Text = String.Format("{0} / {1} Runs Completed", myCount, maxRuns);
                            }
                    ));
                });
        });
    }

person William Riley    schedule 14.01.2014    source источник
comment
У меня нет никакого кода, но iirc вам нужно создать источник отмены, создать токен отмены и передать его в вашу StartTask (рядом с IProgress‹T›), затем вы отмените токен через источник в своем пользовательском интерфейсе и проверьте для IsCancelled в вашем асинхронном методе.   -  person Liath    schedule 14.01.2014
comment
В MSDN есть пример msdn.microsoft.com/en -us/library/dd997396(v=vs.110).aspx   -  person garf1eld    schedule 14.01.2014


Ответы (1)


Лучший способ поддержки отмены — передать CancellationToken методу async. Затем нажатие кнопки может быть связано с отменой токена.

class TheClass
{
  CancellationTokenSource m_source;

  void StartThread() { 
    m_source = new CancellationTokenSource;
    StartThread(m_source.Token);
  }

  private async void StartThread(CancellationToken token) { 
    ...
  }

  private void OnCancelClicked(object sender, EventArgs e) {
    m_source.Cancel();
  }
}

Хотя этого недостаточно. Оба метода startThread и StartProcess необходимо будет обновить, чтобы совместно отменить задачу после того, как CancellationToken зарегистрируется как отмененная.

person JaredPar    schedule 14.01.2014
comment
Хороший ответ, но с кем-то, кто борется так же сильно, как и с ОП, было бы неплохо дать лучшее определение / пример того, что означает «Совместная отмена». - person Scott Chamberlain; 14.01.2014
comment
Спасибо, Джаред, а также Скотт. Я понимаю, что моя ситуация немного менее распространена, чем просто отмена задачи. Я понимаю, что мне нужно убить процесс, который я породил, и завершить задачу. Я просто теряюсь, так как не могу найти в сети ни одной ссылки на подобную ситуацию. - person William Riley; 14.01.2014