sel*_*dog 3 c# task-parallel-library c#-4.0 progress-bar
我试图从小的"块"或页面中读取数据库中的大量行,并将进度报告给用户; 即,如果我在加载每个块时加载100个"块"报告进度.
我在C#4.0中使用TPL从数据库中读取这些块,然后将完整的结果集交给另一个可以使用它的任务.我觉得TPL让我能更好地控制任务取消和切换,而不是BackgroundWorker等,但似乎没有一种内置的方式来报告任务的进度.
这是我实现的向WPF进度条报告进度的解决方案,我想确保这是合适的,并且我应该采取更好的方法.
我首先创建了一个简单的界面来表示不断变化的进度:
public interface INotifyProgressChanged
{
int Maximum { get; set; }
int Progress { get; set; }
bool IsIndeterminate { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
这些属性可以绑定到WPF视图中的ProgressBar,并且接口由支持ViewModel实现,后者负责启动数据加载并最终报告整体进度(本示例简化):
public class ContactsViewModel : INotifyProgressChanged
{
private IContactRepository repository;
...
private void LoadContacts()
{
Task.Factory.StartNew(() => this.contactRepository.LoadWithProgress(this))
.ContinueWith(o => this.UseResult(o));
}
}
Run Code Online (Sandbox Code Playgroud)
您会注意到我将ViewModel作为INotifyProgressChanged传递给存储库方法,这是我想确保我没有做错的地方.
我的思考过程是,为了报告进度,实际执行工作的方法(存储库方法)需要访问INotifyProgressChanged接口,以报告最终更新视图的进度.这里快速浏览一下存储库方法(本例简述):
public class ContactRepository : IContactRepository
{
...
public IEnumberable<Contact> LoadWithProgress(INotifyProgressChanged indicator)
{
var resultSet = new List<Contact>();
var query = ... // This is a LINQ to Entities query
// Set the maximum to the number of "pages" that will be iterated
indicator.Maximum = this.GetNumberOfPages(query.Count(), this.pageSize);
for (int i = 0; i < indicator.Maximum; i++)
{
resultSet.AddRange(query.Skip(i * this.pageSize).Take(this.pageSize));
indicator.Progress += 1; // As each "chunk" is loaded, progress is updated
}
// The complete list is returned after all "chunks" are loaded
return resultSet.AsReadOnly();
}
}
Run Code Online (Sandbox Code Playgroud)
这就是存储库最终通过ViewModel向View报告进度的方式.这是正确的方法吗?我是否正确使用TPL,违反任何主要规则等?这个解决方案正在运行,正在按预期报告进展情况,我只是想确保我没有为失败做好准备.
执行此操作的"规定"方法是将TaskScheduler实例传递TaskSheduler::FromCurrentSynchronizationContext给ContinueWith您希望确保在WPF调度程序线程上执行的实例.
例如:
public void DoSomeLongRunningOperation()
{
// this is called from the WPF dispatcher thread
Task.Factory.StartNew(() =>
{
// this will execute on a thread pool thread
})
.ContinueWith(t =>
{
// this will execute back on the WPF dispatcher thread
},
TaskScheduler.FromCurrentSynchronizationContext());
}
Run Code Online (Sandbox Code Playgroud)
我建议您避免从后台线程更新数据绑定属性.
要解决此问题,您可以让后台任务发布UI任务以进行更新,或者(甚至更好)使用基于任务的异步模式概述文档中描述的IProgress<T>/ Progress<T>system .
这种IProgress<T>方法很好,因为它将您的后台任务与ViewModel更新分开.但是,它有一些缺点(在后台任务和更新之间共享数据;以及处理更新中的异常); 我希望在正式的Async CTP发布之前解决这些问题.
| 归档时间: |
|
| 查看次数: |
4101 次 |
| 最近记录: |