倒置的后台工作者
本文关键字:工作者 后台 | 更新日期: 2023-09-27 18:04:09
我有许多类来做一些事情,通常是遍历一个记录集并为每个记录调用一个或两个web服务。
目前这一切都运行在GUI线程和挂画。第一个想法是使用BackgroundWorker并实现一个不错的进度条,处理错误,完成等。所有后台工作人员能做的好事。
代码一出现在屏幕上就开始发臭了。我在每个类中编写了很多后台工作人员,在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();
}
I think I want:
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();
}
我可以继续这样做,但不想重新发明。
我正在寻找的答案将沿着"是的,Joe做了一个很好的实现(这里)"或"这是一个代理小部件模式,去阅读它(这里)"这条线。
每个操作需要实现以下接口:
/// <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()移动到它自己的类中,并实现取消操作的方法。
我会把我的非ui代码放到一个新类中,并使用线程(而不是后台worker)。要显示进度,让新类将事件发送回UI并使用Dispatcher。调用来更新UI。
这里有一些编码,但它更干净,更有效。并且比使用后台worker(它实际上只用于小任务)更易于维护。