报告异步任务的进度 [英] Reporting progress from Async Task

查看:116
本文介绍了报告异步任务的进度的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

您好,其他开发人员,

我正面临一个小两难境地.我有一个WPF应用程序,该应用程序读取一个相当大的Excel文件,然后将其输出为XML文件.我面临的问题是我想将操作进度报告给处理程序.

I am facing a slight dilemma. I have a WPF application that reads an rather large Excel File and after that it outputs it as an XML file. The problem that I am facing is that I want to report the progress of the operation back to the handler.

我无法让它平稳地"工作,从字面上看,GUI线程中的进度栏​​立即被填充,然后将填充从Excel文件读取的内容的DataGrid填充.

I cannot get it to work "smoothly", literally the Progress Bar that is in the GUI Thread gets filled at once and immediately after that the DataGrid that holds the content read from the Excel File gets filled.

我是Async/Await/Task的新手,直到现在我一直在使用BackgroundWorker,但我希望对此有所了解,因此,如果这是一个愚蠢的问题,我们感到抱歉.

I am new to Async/Await/Task, I've been using BackgroundWorker until now but I wish to gain more knowledge about this, so I'm sorry if this is a dumb question.

我从在这里.

老实说,我不知道进度栏为什么没有平滑地"填充……

I honestly have no idea why the Progress Bar does not fill "smoothly" as it should be...

在GUI中被调用的代码:

Code that is getting called in the GUI:

        var progress = new Progress<int>(progressPercent => pBar.Value = progressPercent);

        Task.Run(() => _sourceService.ReadFileContent(filePath, progress)).ContinueWith(task =>
        {

            dataGrid.ItemsSource = task.Result.DefaultView;
            DataTable = task.Result;

            if (DataTable.Rows.Count > 0)
                BtnCreateXmlFile.IsEnabled = true;

        }, CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.FromCurrentSynchronizationContext());

ReadFileContent(filePath, progress)的方法主体:

public DataTable ReadFileContent(string filePath, IProgress<int> progress)
    {
        var rowStart = 7;
        var columnStart = 1;

        var existingFile = new FileInfo(filePath);
        using (var package = new ExcelPackage(existingFile))
        {
            var worksheet = package.Workbook.Worksheets["BOP"];
            var dt = new DataTable();

            // Compose the name of the table from:
            // - Product Type
            // - Customer
            // - Customer Project
            // * These can be found in the "CollaborationContext" sheet that is present in the ExcelFile.
            if (package.Workbook.Worksheets["CollaborationContext"].SelectedRange["B6"].Value.Equals("object_type"))
            {
                dt.TableName = package.Workbook.Worksheets["CollaborationContext"].SelectedRange["C9"].Value + " " +
                               package.Workbook.Worksheets["CollaborationContext"].SelectedRange["D9"].Value + " " +
                               package.Workbook.Worksheets["CollaborationContext"].SelectedRange["E9"].Value;
            }


                dt.TableName = package.Workbook.Worksheets["CollaborationContext"].SelectedRange["B9"].Value + " " + 
                               package.Workbook.Worksheets["CollaborationContext"].SelectedRange["C9"].Value + " " +
                               package.Workbook.Worksheets["CollaborationContext"].SelectedRange["D9"].Value;


            // Report only in chunks. We do not want to call `progress.Report` for each row that is read.
            var totalRows = worksheet.Dimension.End.Row;
            var currentIndex = 0;
            var percentageProgress = totalRows / 10;


            // Get Columns and add them to the DataTable
            for (var col = columnStart; col <= worksheet.Dimension.End.Column - 1; col++)
                dt.Columns.Add(worksheet.Cells[6, col].Value.ToString());

            // Place data into DataTable
            for (var row = rowStart; row <= worksheet.Dimension.End.Row; row++)
            {
                var dr = dt.NewRow();
                var x = 0;
                currentIndex++;

                for (var col = columnStart; col <= worksheet.Dimension.End.Column - 1; col++)
                {
                    dr[x++] = worksheet.Cells[row, col].Value;
                }
                dt.Rows.Add(dr);

                // Report progress back to the handler
                if (currentIndex % percentageProgress == 0)
                    progress?.Report(row);
            }

            return dt;
        } 

    }

提前谢谢!

推荐答案

首先,让我们摆脱危险的ContinueWith调用.您确实应该使用await代替:

First, let's get rid of the dangerous ContinueWith call. You really should use await instead:

var progress = new Progress<int>(progressPercent => pBar.Value = progressPercent);
var result = await Task.Run(() => _sourceService.ReadFileContent(filePath, progress));
dataGrid.ItemsSource = result.DefaultView;
DataTable = result;
if (DataTable.Rows.Count > 0)
  BtnCreateXmlFile.IsEnabled = true;

接下来,您可能会看到进度上的问题是您的Progress<int>处理程序期望的是progressPercent,但是ReadFileContent发送的是要读取的行数,而不是 .因此,要解决此问题:

Next, the problem you're likely seeing with progress is that your Progress<int> handler is expecting a progressPercent, but ReadFileContent is sending how many rows it's read, not a percentage. So, to fix that:

if (currentIndex % percentageProgress == 0)
  progress?.Report(row * 100 / totalRows);

(这里还有其他几种选择;例如,如果您想使用更高级的用户界面,则可以决定报告当前行行).

(there are several other options here; e.g., you could decide to report the current row and the total rows if you want a fancier UI).

进度栏无法平滑地"填充

the Progress Bar does not fill "smoothly" as it should be

我上面描述的是最小可接受的解决方案.特别是,仅更新10次"代码有点问题;无论更新的速度有多快,它总是会更新10次.更为通用的解决方案是使用基于Rx的IProgress解决方案,该解决方案可让您根据时间(例如,每秒4次更新)进行调节.

What I've described above is the minimum acceptable solution. In particular, the "only update 10 times" code is a bit problematic; it always updates 10 times, regardless of how fast or slow the updates are. A more general solution would be to use an Rx-based IProgress solution, which would allow you to throttle based on time (e.g., 4 updates per second).

这篇关于报告异步任务的进度的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆