不知怎的,我无法相信我是第一个遇到这个问题的人(而且我不想相信我是唯一一个不能直接看到解决方案的傻瓜),但是我的搜索不是足够强大。
当我需要一个接一个地执行一些耗时的步骤时,我经常遇到这种情况。工作流程看起来像
var data = DataGetter.GetData();
var processedData = DataProcessor.Process(data);
var userDecision = DialogService.AskUserAbout(processedData);
// ...
我不希望在每个步骤中阻止UI,因此每个方法都会立即返回,并在完成后引发一个事件。现在随之而来的是欢闹,因为上面的代码块变异为
DataGetter.Finished += (data) =>
{
DataProcessor.Finished += (processedData) =>
{
DialogService.Finished(userDecision) =>
{
// ....
}
DialogService.AskUserAbout(processedData);
}
DataProcessor.Process(data);
};
DataGetter.GetData();
对于我的口味,这读起来太像Continuation-passing style,并且必须有更好的方法来构造这段代码。但是如何?
答案 0 :(得分:7)
正确的方法是以同步方式设计组件并在后台线程中执行完整的链。
答案 1 :(得分:4)
Task Parallel Library对此类代码非常有用。请注意,TaskScheduler.FromCurrentSynchronizationContext()可用于在UI线程上运行任务。
Task<Data>.Factory.StartNew(() => GetData())
.ContinueWith(t => Process(t.Result))
.ContinueWith(t => AskUserAbout(t.Result), TaskScheduler.FromCurrentSynchronizationContext());
答案 2 :(得分:2)
您可以将所有内容放入BackgroundWorker中。只有将GetData,Process和AskUserAbout方法更改为同步运行时,以下代码才能正常工作。
这样的事情:
private BackgroundWorker m_worker;
private void StartWorking()
{
if (m_worker != null)
throw new InvalidOperationException("The worker is already doing something");
m_worker = new BackgroundWorker();
m_worker.CanRaiseEvents = true;
m_worker.WorkerReportsProgress = true;
m_worker.ProgressChanged += worker_ProgressChanged;
m_worker.DoWork += worker_Work;
m_worker.RunWorkerCompleted += worker_Completed;
}
private void worker_Work(object sender, DoWorkEventArgs args)
{
m_worker.ReportProgress(0, "Getting the data...");
var data = DataGetter.GetData();
m_worker.ReportProgress(33, "Processing the data...");
var processedData = DataProcessor.Process(data);
// if this interacts with the GUI, this should be run in the GUI thread.
// use InvokeRequired/BeginInvoke, or change so this question is asked
// in the Completed handler. it's safe to interact with the GUI there,
// and in the ProgressChanged handler.
m_worker.ReportProgress(67, "Waiting for user decision...");
var userDecision = DialogService.AskUserAbout(processedData);
m_worker.ReportProgress(100, "Finished.");
args.Result = userDecision;
}
private void worker_ProgressChanged(object sender, ProgressChangedEventArgs args)
{
// this gets passed down from the m_worker.ReportProgress() call
int percent = args.ProgressPercentage;
string progressMessage = (string)args.UserState;
// show the progress somewhere. you can interact with the GUI safely here.
}
private void worker_Completed(object sender, RunWorkerCompletedEventArgs args)
{
if (args.Error != null)
{
// handle the error
}
else if (args.Cancelled)
{
// handle the cancellation
}
else
{
// the work is finished! the result is in args.Result
}
}