如何在窗体关闭时停止异步方法

3

我有一个表单,用于下载货币汇率,该方法是异步运行的。现在,当用户关闭窗口时,我需要停止该方法。我在这里找到了一个解决方案(链接),但是我没有成功将其实施到我的代码中。以下是我的完整代码:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Globalization;
using System.Xml;
using System.Data.SqlClient;
using System.Threading;

namespace ProjectPro
{
    public partial class GetRatesForPeriod : Form
    {
        public GetRatesForPeriod()
        {
            InitializeComponent();
        }

        private void closeBut_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        CancellationTokenSource cts;
        private async void runBut_Click(object sender, EventArgs e)
        {
            try
            {
                cts = new CancellationTokenSource();

                this.runBut.Enabled = false;

                for (DateTime d = fromDatePicker.Value.Date; d <= toDatePicker.Value.Date; d = d.AddDays(1))
                {
                    if (d.DayOfWeek == DayOfWeek.Saturday || d.DayOfWeek == DayOfWeek.Sunday)
                        continue;

                    switch(StaticValues.user.Language)
                    {
                        case "English":
                            this.statusLab.Text = "Updating rates: " + d.Date.ToShortDateString();
                            break;
                        case "Russian":
                            this.statusLab.Text = "Обновление курса валют: " + d.Date.ToShortDateString();
                            break;
                        case "Azeri":
                            this.statusLab.Text = "Məzənnələr yenilənir: " + d.Date.ToShortDateString();
                            break;
                        default:
                            break;
                    }



                    string url = "http://cbar.az/currencies/" + d.ToString("dd.MM.yyyy", CultureInfo.InvariantCulture) + ".xml";

                    #region read rates for the date to the DataTable

                    var slowTask = Task<XmlDocument>.Factory.StartNew(() => GetXmlAsync(url, cts.Token));

                    await slowTask;

                    XmlDocument doc = slowTask.Result;

                    XmlElement root = doc.DocumentElement;
                    XmlNodeList nodes = root.SelectNodes("//ValCurs/ValType");

                    DataTable tempRates = new DataTable();

                    foreach (XmlNode node in nodes)
                    {
                        if (node.Attributes["Type"].Value == "Xarici valyutalar")
                        {
                            //create temp table and load new rates
                            tempRates.Clear();
                            tempRates.Columns.Add("Code");
                            tempRates.Columns.Add("Nominal");
                            tempRates.Columns.Add("Name");
                            tempRates.Columns.Add("Value");

                            foreach (XmlNode currency in node.ChildNodes)
                            {
                                DataRow dr = tempRates.NewRow();
                                dr["Code"] = currency.Attributes["Code"].Value;

                                foreach (XmlNode currencyDetailsNode in currency.ChildNodes)
                                {
                                    dr[currencyDetailsNode.Name] = currencyDetailsNode.InnerText;
                                }

                                tempRates.Rows.Add(dr);
                            }
                        }
                    }
                    #endregion

                    DAL dal = new DAL();
                    dal.ClearCurrentRates(d);

                    //insert new values
                    foreach (DataRow currencyRow in StaticValues.dataSet.Tables["Currencies"].Rows)
                    {
                        if (currencyRow["Code"].ToString() == "AZN")
                        {
                            #region Insert the row for AZN
                            try
                            {
                                SqlParameter[] pars = new SqlParameter[3];

                                pars[0] = new SqlParameter("@Date", SqlDbType.Date);
                                pars[0].Value = d.ToShortDateString();

                                pars[1] = new SqlParameter("@currencyCode", SqlDbType.NVarChar);
                                pars[1].Value = currencyRow[1].ToString();

                                pars[2] = new SqlParameter("@Rate", SqlDbType.Decimal);
                                pars[2].Value = 1;

                                dal.InsertData("CurrencyRates", pars);
                            }
                            catch (Exception ex)
                            {
                                StaticValues.WriteEventLogXML(ex, this.Text);
                                switch (StaticValues.user.Language)
                                {
                                    case "English":
                                        MessageBox.Show("Database error", Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error);
                                        break;
                                    case "Russian":
                                        MessageBox.Show("Ошибка базы данных", Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error);
                                        break;
                                    case "Azeri":
                                        MessageBox.Show("Məlumat bazası səhvi", Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error);
                                        break;
                                    default:
                                        break;
                                }
                            }
                            #endregion
                            continue;
                        }
                        foreach (DataRow tempRow in tempRates.Rows)
                        {
                            if (tempRow["Code"].ToString() == currencyRow["Code"].ToString())
                            {
                                #region Insert the row
                                try
                                {
                                    SqlParameter[] pars = new SqlParameter[3];

                                    pars[0] = new SqlParameter("@Date", SqlDbType.Date);
                                    pars[0].Value = d.ToShortDateString();

                                    pars[1] = new SqlParameter("@currencyCode", SqlDbType.NVarChar);
                                    pars[1].Value = currencyRow[1].ToString();

                                    pars[2] = new SqlParameter("@Rate", SqlDbType.Decimal);
                                    pars[2].Value = decimal.Parse(tempRow["Value"].ToString(), CultureInfo.InvariantCulture);

                                    dal.InsertData("CurrencyRates", pars);
                                    break;
                                }
                                catch (Exception ex)
                                {
                                    StaticValues.WriteEventLogXML(ex, this.Text);
                                    switch (StaticValues.user.Language)
                                    {
                                        case "English":
                                            MessageBox.Show("Database error", Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error);
                                            break;
                                        case "Russian":
                                            MessageBox.Show("Ошибка базы данных", Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error);
                                            break;
                                        case "Azeri":
                                            MessageBox.Show("Məlumat bazası səhvi", Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error);
                                            break;
                                        default:
                                            break;
                                    }
                                    break;
                                }
                                #endregion
                            }
                        }
                    }
                    Thread.Sleep(1000);
                }
            }
            catch (OperationCanceledException ex)
            {
                StaticValues.WriteEventLogXML(ex, this.Text);
                switch (StaticValues.user.Language)
                {
                    case "English":
                        this.statusLab.Text = "Cancelled";
                        MessageBox.Show(ex.Message, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error);
                        break;
                    case "Russian":
                        this.statusLab.Text = "Готово";
                        MessageBox.Show(ex.Message, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error);
                        break;
                    case "Azeri":
                        this.statusLab.Text = "Dayandırılıb.";
                        MessageBox.Show(ex.Message, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error);
                        break;
                    default:
                        break;
                }
            }
            catch (Exception ex)
            {
                StaticValues.WriteEventLogXML(ex, this.Text);
                switch (StaticValues.user.Language)
                {
                    case "English":
                        MessageBox.Show(ex.Message, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error);
                        break;
                    case "Russian":
                        MessageBox.Show(ex.Message, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error);
                        break;
                    case "Azeri":
                        MessageBox.Show(ex.Message, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error);
                        break;
                    default:
                        break;
                }
            }
            finally
            {
                switch (StaticValues.user.Language)
                {
                    case "English":
                        this.statusLab.Text = "Ready";
                        break;
                    case "Russian":
                        this.statusLab.Text = "Готово";
                        break;
                    case "Azeri":
                        this.statusLab.Text = "Hazır";
                        break;
                    default:
                        break;
                }
                this.runBut.Enabled = true;
            }            
        }


        private XmlDocument GetXmlAsync(string url, CancellationToken tok)
        {
            XmlDocument doc = new XmlDocument();
            doc.Load(url);

            return doc;
        }

        private void GetRatesForPeriod_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (cts != null)
                cts.Cancel();
        }
    }
}

在 cts.Cancel(); 这一行上设置断点,确保它是否被执行,同时你可以订阅 Form.Closing 事件。 - Jamaxack
这是一段相当长的代码,但我只能在内部任务中看到你使用cts.Token,当你取消时,应该添加一些代码来跳出循环!另外为什么要使用Thread.Sleep?这样至少会有几秒的等待时间。 - Random Dev
@Jamaxack,我在那里设置了一个断点,并且它被触发了。我还添加了Form.Closing,谢谢你的提醒。 - Emil Huseynov
@Carsten,我需要 Thread.Sleep,否则服务器会因某种原因(可能是安全性)返回错误。 - Emil Huseynov
1个回答

0
问题出在这里:
    private XmlDocument GetXmlAsync(string url, CancellationToken tok)
    {
        XmlDocument doc = new XmlDocument();
        doc.Load(url);

        return doc;
    }

XmlDocument不是异步的,也没有使用取消令牌。您需要修改这个方法以异步加载URL。一个候选方案可能是使用HttpClient。我在这里找到了一个例子,可能会有所帮助。


如果你能帮我解决这个问题,我将非常感激。不幸的是,我对此毫无经验...它成功加载了汇率,并且窗口反应灵敏,没有冻结,所以我猜异步方法本身运行良好。但是当我关闭表单时,汇率仍然在更新。 - Emil Huseynov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接