在多线程编程或技术方面没有经验,我想将这个问题作为一种方法来集中设计,以实现对于相对长时间运行的作业(4到10秒)的实际启动一些用户采取行动的时间:
我开始尝试使用子类或其他封装的BackgroundWorker的路径,但发现自己被绊倒了如何同步计算定时器线程上的经过秒数的概念,而另一个线程正在进行工作。
可能导致更集中的编程问题的设计理念非常受欢迎!
干杯,
Berryl
答案 0 :(得分:2)
使用DispatcherTimer更新视图模型中的ElapsedTime属性。这是从GUI线程调用的,因此您可以直接绑定到该属性。使用BackgroundWorker执行线程任务。
public class ViewModel : INotifyPropertyChanged
{
public DateTime ElapsedTime {get; private set;}
public bool IsRunning {get; private set;}
private BackgroundWorker worker = new BackgroundWorker();
private DateTime startTime;
private DispatcherTimer t = new DispatcherTimer();
public ViewModel()
{
t.Interval = 500;
t.Tick += (ox,ex) => UpdateTime();
worker.DoWork += YourMethodHere;
worker.RunWorkerCompleted += (ox,ex)=> {
IsRunning = false;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("IsRunning"));
};
}
public void UpdateTime()
{
ElapsedTime=startTime.Subtract(DateTime.Now);
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("ElapsedTime"));
}
public void Start()
{
startTime=DateTime.Now;
worker.RunWorkerAsync();
IsRunning = true;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("IsRunning"));
}
}
您可以将进度条GUI元素绑定到视图模型中的bool属性,该属性在启动和完成后台任务时更新(使用RunWorkerComplete事件)。
答案 1 :(得分:2)
BackgroundWorker
类是开启后台任务的绝佳工具,但它的设计迫使开发人员走错路径,有时候应该如何更新UI。问题是它使用推模型来更新UI。换句话说,ReportProgress
和ProgressChanged
成员旨在将事件处理程序封送到UI线程上。这对许多情况都很好,但在大多数其他情况下它不能很好地工作......正如你所注意到的那样。
更新UI的另一种策略是让UI线程定期轮询共享数据结构以获取进度信息。工作线程将向此共享数据结构发布新的进度信息,UI线程将按照自己的计划进行提取。这有几个好处。
Control.Invoke
强加的UI和工作线程之间的紧密耦合。IsBusy
数据绑定属性要容易得多。如果要切换到轮询方法,则必须废弃BackgroundWorker
类并手动启动新线程(或使用ThreadPool
)。
答案 2 :(得分:0)
我的工作线程使用回调方法来更新包含进度和消息的UI线程中的集合,并在UI线程中使用单独的计时器来更新UI,我的结果很好。在集合周围使用锁定来考虑并发性。回调函数还会向工作线程返回一个继续/停止标志,以实现正常的关闭控制。