在多种情况下报告进度:控制台应用程序,Windows窗体

时间:2019-06-03 12:41:28

标签: c#

在多种情况下报告进度:控制台应用程序,Windows窗体。

上下文:

我们有一些工作流程,该流程将查找要处理的数据并对其进行处理。 在Task Scheduler或Windows Service中使用它们。调用Run()不会得到任何回报。这是从邮件,数据库等处获取数据的过程的基本示例。做点什么。保存已处理。失败时发出警报。

但是对于Gui的Console,我们将需要Run进程来反馈其进度。

public interface IProcess
{
    bool Init(out string error);
    bool Init(Configuration config, out string error);

    bool Run(out ErrorCode errorCode);
    bool Run(object Item, out ErrorCode errorCode);

    bool IsInitialized { get; set; }
    bool IsDebugMode { get; set; }
}

public class SO_ExempleProcess : IProcess
{

    public bool Run(out ErrorCode errorCode)
    {
        errorCode = new ErrorCode(1, "");

        var items = db.Select(x=> );

        if (!items.Any())
        {
            Logger.Debug("No data to integrate. ");
            errorCode = new ErrorCode(2, "");
            return true;
        }

        foreach (var item in items)
        {
            var foo = SubStep(item);
            var isGood = MainStep(foo, item);

            if (!isGood)
            {
                Logger.Alert("Error processing item:", item);
                continue;
            }

            item.ProcessTicket = true;
            item.ProcessBy = "From, API key, Instance(Scheduler, Service, Gui, Console, Debug)";
            item.ProcessDate = DateTime.Now;
        }

        db.SaveChanges();
        return true;
    }
}

对于Gui,我将添加一名后台工作人员ReportProgress()
对于控制台,我将添加以下内容:

decimal total = items.Count(); int last = 0; int index = 0;
Console.WriteLine($"> SO process name ");
Console.WriteLine("[InProgress]");
Console.Write(">");


foreach (var item in items)
{
    //process

    var percent = (index / total) * 100;
    if ((percent / 10) > last)
    {
        var _new = decimal.ToInt32(percent / 10);
        Console.Write(new String('#', _new - last));
        last = _new;
    }
}
Console.WriteLine("\n> 100%");
Console.WriteLine("[Completed]");

我想装饰我的接口和类,这样我就可以轻松地报告任何进度。无需修改Run to mutch。

1 个答案:

答案 0 :(得分:1)

让我们考虑一个长时间运行的操作,该操作处理一堆图像。

foreach(Image image in myImages) {
    processImage(image);
}

要使此代码具有报告进度的功能,我们将与进度对象进行通信并定期进行报告。已经有许多设计决策。也许最重要的是:我怎么知道要提前完成多少工作?如果我知道,那么我可以先报告有多少个项目,然后每次要做某事时,我可以说其中一项是完整的。或者,我可以只报告一个百分比。通常,进度接口会执行前者。因此,我们至少需要修改代码以报告进度,就像这样。

progress.ExpectSteps(myImages.Length);
foreach(Image image in myImages) {
    processImage(image);
    progress.CompleteStep();
}

在这里,根据这个最小接口(我自己设计的),我将其分为两个呼叫。

public interface IProgress
{
    /// <summary>
    /// Call this first to indicate how many units of work are expected to be completed.
    /// </summary>
    /// <param name="numberOfStepsToExpect">The number of units of work to expect.</param>
    void ExpectSteps(int numberOfStepsToExpect);

    /// <summary>
    /// Call this each time you complete a unit of work.
    /// </summary>
    void CompleteStep();
}

调用函数时,将向其传递一个抽象化报告进度行为的对象。

void ProcessImages(IProgress progress) {
    progress.ExpectSteps(myImages.Length);
    foreach(Image image in myImages) {
        processImage(image);
        progress.CompleteStep();
    }
}

现在要实现提供IProgress接口的对象,我们需要做出另一个决定:哪个线程将调用方法?如果您完全控制线程,那么您可能知道它将永远是调用接口的辅助线程。然后,该实现将必须调用 dispatch 返回用户界面线程的方法,该方法将向用户显示进度摘要。如果要使代码可以在主线程上运行更加笼统,那么您可能必须精明并利用InvokeRequired之类的功能。

但基本上,根据调用该方法的接口,它可以提供实现IProgress并调用ProcessImages函数的对象。

这是一个不支持线程的控制台应用程序的示例实现,其中所有工作都在主线程上完成。

internal class ProgressReport : IProgress
{
    int numberOfStepsToExpect = 100;
    int numberOfStepsCompleted = 0;

    void IProgress.ExpectSteps(int numberOfStepsToExpect)
    {
        this.numberOfStepsToExpect = numberOfStepsToExpect;
    }

    void IProgress.CompleteStep()
    {
        ++numberOfStepsCompleted;
        ReportProgress();
    }

    private void ReportProgress()
    {
        Console.WriteLine("{0}% complete", 100 * numberOfStepsCompleted / numberOfStepsToExpect);
    }
}

这是不支持多线程的实现,并且是自定义Windows窗体对话框。

int numberOfStepsCompleted = 0;
int numberOfStepsToExpect = 100;

void IProgress.CompleteStep()
{
    if (this.InvokeRequired)
    {
        Invoke((Action) delegate { ((IProgress)this).CompleteStep(); });
        return;
    }

    ++numberOfStepsCompleted;
}

void IProgress.ExpectSteps(int numberOfStepsToExpect)
{
    if (this.InvokeRequired)
    {
        Invoke((Action)delegate { ((IProgress)this).ExpectSteps(numberOfStepsToExpect); });
        return;
    }
    this.numberOfStepsToExpect = numberOfStepsToExpect;
    ReportProgress();
}

void ReportProgress()
{
    this.label1.Text = string.Format("{0}% complete", 100 * numberOfStepsCompleted / numberOfStepsToExpect));
}