【问题标题】:Inverted background worker倒置后台工作者
【发布时间】:2011-08-11 23:42:30
【问题描述】:

我有许多做事的类,通常单步执行一个记录集并为每条记录调用一个或两个 Web 服务。

目前这一切都在 GUI 线程中运行并挂起绘画。第一个想法是使用 BackgroundWorker 并实现一个漂亮的进度条、处理错误、完成等。Background Worker 启用的所有好东西。

代码一进入屏幕就开始闻起来。我在每个类中编写了很多后台工作程序,在 bw_DoWork 方法中重复了大部分 ProcessRows 方法,并认为应该有更好的方法,而且可能已经完成了。

在我开始重新发明轮子之前,是否有一个模式或实现用于分离后台工作人员的类?它需要实现接口(如 ibackgroundable)的类,但这些类仍然可以独立运行,并且只需要极少的更改即可实现接口。

编辑:@Henk 要求的简化示例:

我有:

    private void buttonUnlockCalls_Click(object sender, EventArgs e)
    {
        UnlockCalls unlockCalls = new UnlockCalls();
        unlockCalls.MaxRowsToProcess = 1000;
        int processedRows = unlockCalls.ProcessRows();
        this.textProcessedRows.text = processedRows.ToString();
    }

我想我想要:

    private void buttonUnlockCalls_Click(object sender, EventArgs e)
    {
        UnlockCalls unlockCalls = new UnlockCalls();
        unlockCalls.MaxRowsToProcess = 1000;

        PushToBackground pushToBackground = new PushToBackground(unlockCalls)
        pushToBackground.GetReturnValue = pushToBackground_GetReturnValue;
        pushToBackground.DoWork();
    }

    private void pushToBackground_GetReturnValue(object sender, EventArgs e)
    {
        int processedRows = e.processedRows;
        this.textProcessedRows.text = processedRows.ToString();
    }

我可以继续这样做,但不想重新发明。

我正在寻找的答案类似于“是的,乔做了一个很好的实现(这里)”或“这是一个代理小部件模式,去阅读它(这里)”

【问题讨论】:

  • 也许添加一个小(简化)示例?
  • 已添加,在此示例中,UnlockCalls 使用 SQL 语句抓取多达 1000 行,使用 Web 服务神奇地解锁每一行,并返回它处理的行数。我还想在 pushToBackground 类中实现/使用取消和进度功能。

标签: c# multithreading backgroundworker


【解决方案1】:

每个操作都需要实现如下接口:

/// <summary>
/// Allows progress to be monitored on a multi step operation
/// </summary>
interface ISteppedOperation
{
    /// <summary>
    /// Move to the next item to be processed.
    /// </summary>
    /// <returns>False if no more items</returns>
    bool MoveNext();

    /// <summary>
    /// Processes the current item
    /// </summary>
    void ProcessCurrent();

    int StepCount { get; }
    int CurrentStep { get; }
}

这将步骤的枚举与处理分开。

这是一个示例操作:

class SampleOperation : ISteppedOperation
{
    private int maxSteps = 100;

    //// The basic way of doing work that I want to monitor
    //public void DoSteppedWork()
    //{
    //    for (int currentStep = 0; currentStep < maxSteps; currentStep++)
    //    {
    //        System.Threading.Thread.Sleep(100);
    //    }
    //}

    // The same thing broken down to implement ISteppedOperation
    private int currentStep = 0; // before the first step
    public bool MoveNext()
    {
        if (currentStep == maxSteps)
            return false;
        else
        {
            currentStep++;
            return true;
        }
    }

    public void ProcessCurrent()
    {
        System.Threading.Thread.Sleep(100);
    }

    public int StepCount
    {
        get { return maxSteps; }
    }

    public int CurrentStep
    {
        get { return currentStep; }
    }

    // Re-implement the original method so it can still be run synchronously
    public void DoSteppedWork()
    {
        while (MoveNext())
            ProcessCurrent();
    }
}

这可以从如下形式调用:

private void BackgroundWorkerButton_Click(object sender, EventArgs eventArgs)
{
    var operation = new SampleOperation();

    BackgroundWorkerButton.Enabled = false;

    BackgroundOperation(operation, (s, e) =>
        {
            BackgroundWorkerButton.Enabled = true;
        });
}

private void BackgroundOperation(ISteppedOperation operation, RunWorkerCompletedEventHandler runWorkerCompleted)
{
    var backgroundWorker = new BackgroundWorker();

    backgroundWorker.RunWorkerCompleted += runWorkerCompleted;
    backgroundWorker.WorkerSupportsCancellation = true;
    backgroundWorker.WorkerReportsProgress = true;

    backgroundWorker.DoWork += new DoWorkEventHandler((s, e) =>
    {
        while (operation.MoveNext())
        {
            operation.ProcessCurrent();

            int percentProgress = (100 * operation.CurrentStep) / operation.StepCount;
            backgroundWorker.ReportProgress(percentProgress);

            if (backgroundWorker.CancellationPending) break;
        }
    });

    backgroundWorker.ProgressChanged += new ProgressChangedEventHandler((s, e) =>
    {
        var progressChangedEventArgs = e as ProgressChangedEventArgs;
        this.progressBar1.Value = progressChangedEventArgs.ProgressPercentage;
    });

    backgroundWorker.RunWorkerAsync();
}

我还没有这样做,但我将把 BackgroundOperation() 移到它自己的一个类中,并实现取消操作的方法。

【讨论】:

    【解决方案2】:

    我会将我的非 UI 代码放入一个新类并使用线程(不是后台工作人员)。要显示进度,请让新类触发事件返回 UI,并使用 Dispatcher.Invoke 更新 UI。

    这里有一些编码,但它更干净且有效。并且比使用后台工作程序(仅适用于小任务)更易于维护。

    【讨论】:

    • 正如我所说,“在我重新发明轮子之前”。如果我正在重新发明,使用线程将是一个很好的方法。
    猜你喜欢
    • 2011-07-29
    • 1970-01-01
    • 2011-10-30
    • 2011-03-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多