【问题标题】:Stop async method while form is closed表单关闭时停止异步方法
【发布时间】:2020-07-17 09:46:19
【问题描述】:

我有 3 个 DataGridView,它们在运行时最多加载 20,000 行(从文件中读取行并将每行加载到三个 DataGridView 中的两个中)。我使用线程使加载方法异步,因为在更改它之前,如果我在主线程中加载了太多行,GUI 会冻结。代码按预期工作。 我可以看到数据网格视图中填充的行,并且可以在读取文件并继续阅读时滚动数据网格视图。

问题是当我关闭表单时,我收到“对象已处理期望”。 请你能帮我解决这个问题吗?我可以捕获异常并关闭表单,但这不是合适的方式。我尝试使用取消令牌源,但是当我再次进入循环时,IsCancelled 属性为 true,即使我添加了一个 if 来检查它,它也会在 Invoke 方法中输入操作。

    readonly CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
    Task task;

    private async void UmpiresList_Load(object sender, EventArgs e)
    {
        Logger.LogDebug("Loading results to dataGridView");
        task = Task.Factory.StartNew(() => LoadResults(), cancellationTokenSource.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
        await task.ContinueWith(result =>
        {
            exportToExcelBtn.SetEnabledStatus(true);
            copyToClipboardButton.SetEnabledStatus(true);
        });
    }


    //load results to three tabs -> three data grid views
    private void LoadResults()
    {
        foreach(var result in Results)
        {
            Logger.LogDebug($"Adding row {result.Key}");
            AddUmpiresResultsToDataGrid();
            AddFinalResultToDataGrid();
        }
    }

    //load results to DataGrid A & B
    private void AddUmpiresResultsToDataGrid()
    {
        //umpires.Count <= 5
        foreach(var umpire in umpires)
        {
            AddToAllResultsDataGrid(umpire);
            AddToUmpiresResultsDataGrid(umpire);
        }
    }

    //load results to DataGrid A & C
    private void AddFinalResultToDataGrid(int round, MatchResult matchResult)
    {
        AddToAllResultsDataGrid();
        AddToFinalResultsDataGrid();
    }

   
    //load results to DataGrid A
    private void AddToAllResultsDataGrid()
    {
       if(cancellationTokenSource.IsCancellationRequested==false){
        {
        this.Invoke(new Action(() =>
        allResultsDGV.Rows.Add(round, competitionMode.Name(), result, extraInfo)));
        }
   }

    //load results to DataGrid B
    private void AddToUmpiresResultsDataGrid()
    {
       if(cancellationTokenSource.IsCancellationRequested==false){
        this.Invoke(new Action(() =>
        umpiresResultsDGV.Rows.Add(round, competitionMode.Name(),
                           umpirePoints.Name, umpirePoints.WinnerMessage,
                           umpirePoints.RedPoints, umpirePoints.BluePoints)));
       }
    }

   //load results to DataGrid C
    private void AddToFinalResultsDataGrid()
    {
       if(cancellationTokenSource.IsCancellationRequested==false){
       {
        this.Invoke(new Action(() =>
        finalResultsDGV.Rows.Add(round,
            matchResult.Time.ToString(Logger.DTFormat),
            matchResult.CompetitionMode.Name(),
            matchResult.GetWinnerMessage(),
            matchResult.Red, matchResult.Tie, matchResult.Blue, info1, info2)));
       }
     }

   Form_Closing(Object o, Sender e){
             //this should be enough to cancel? how can I abort the async thread?
            cancellationTokenSource.Cancel();
 }

【问题讨论】:

  • 简单地说:在任务完成之前不要关闭表单。
  • @Sinatr 感谢您的快速回复。我不能,因为客户可以决定中止操作。加载 20,000 行只是一个测试,但如果他加载 5,000 个结果,则可能需要几分钟,也许他决定关闭表单并稍后检查结果。
  • 当您关闭表单时,您向任务发出信号表明它应该停止,但您并不是在等待任务停止。
  • @Neil 正确。因为我不想等。当用户决定中止操作时,可能需要 5 分钟才能完成。我需要立即完成它,因为表单将被关闭并且加载的数据不相关。我该怎么做?
  • 不要通过一个不间断的方法调用加载所有结果。将大作业拆分成较小的批次,并在开始下一批之前检查是否取消。加载 5000 个结果 = 100 个加载 50 个结果。在关闭表单时,您会取消任务,等到它完成后再关闭。

标签: c# multithreading async-await task


【解决方案1】:

我添加了以下代码来解决这个问题。我每 50 行运行一次长操作异步。每项任务完成后,我检查用户是否请求关闭表单。 这是关于性能的一个很好的解决方案?我可以将 loadSegmentLimit 更改为 1 吗?运行 20,000 个任务或 20,000/50 个任务有区别吗?

private async void UmpiresList_Load(object sender, EventArgs e)
    {
        Logger.LogDebug("Loading results to dataGridView");
        while(Results.Count > 0)
        {
            if(closePending)
            {
                closePending = false;
                this.Close();
                return;
            }
           
            task = Task.Factory.StartNew(() => LoadResults(), new CancellationToken(), TaskCreationOptions.LongRunning, TaskScheduler.Default);
            await task.ContinueWith(result =>
            {
                exportToExcelBtn.SetEnabledStatus(true);
                copyToClipboardButton.SetEnabledStatus(true);
            });
        }
    }



 bool closePending = false;
        private void ResultsListForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            if(!task.IsCompleted)
            {
                closePending = true;
                e.Cancel = true;
            }
        }

 private void LoadResults()
        {
            int i = 1;
            foreach(var result in Results.Values.ToList())
            {
                if(i <= loadSegmentLimit)
                {
                    Logger.LogDebug($"Adding row {i}");
                    AddUmpiresResultsToDataGrid(i, result);
                    AddFinalResultToDataGrid(i, result);
                    Results.Remove(i);
                    i++;
                }
            }
        }

【讨论】:

    猜你喜欢
    • 2015-10-18
    • 1970-01-01
    • 1970-01-01
    • 2013-03-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多