我有一个返回我的业务对象的IEnumerable的方法。在此方法中,我将大文本文件的内容解析为业务对象模型。它没有线程化的东西。
在我的ViewModel(WPF)中,我需要存储并显示方法的结果。 Store是一个ObservableCollection。
这是可观察的代码:
private void OpenFile(string file)
{
_parser = new IhvParser();
App.Messenger.NotifyColleagues(Actions.ReportContentInfo, new Model.StatusInfoDisplayDTO { Information = "Lade Daten...", Interval = 0 });
_ihvDataList.Clear();
var obs = _parser.ParseDataObservable(file)
.ToObservable(NewThreadScheduler.Default)
.ObserveOnDispatcher()
.Subscribe<Ihv>(AddIhvToList, ReportError, ReportComplete);
}
private void ReportComplete()
{
App.Messenger.NotifyColleagues(Actions.ReportContentInfo, new Model.StatusInfoDisplayDTO { Information = "Daten fertig geladen.", Interval = 3000 });
RaisePropertyChanged(() => IhvDataList);
}
private void ReportError(Exception ex)
{
MessageBox.Show("...");
}
private void AddIhvToList(Ihv ihv)
{
_ihvDataList.Add(ihv);
}
这是解析器代码:
public IEnumerable<Model.Ihv> ParseDataObservable(string file)
{
using (StreamReader reader = new StreamReader(file))
{
var head = reader.ReadLine(); //erste Zeile ist Kopfinformation
if (!head.Contains("BayBAS") || !head.Contains("2.3.0"))
{
_logger.ErrorFormat("Die Datei {0} liegt nicht im BayBAS-Format 2.3.0 vor.");
}
else
{
while (!reader.EndOfStream)
{
var line = reader.ReadLine();
if (line.Length != 1415)
{
_logger.ErrorFormat("Die Datei {0} liegt nicht im BayBAS-Format 2.3.0 vor.");
break;
}
var tempIhvItem = Model.Ihv.Parse(line);
yield return tempIhvItem;
}
reader.Close();
}
}
}
为什么我没有得到异步的结果?在我的DataGrid中看到结果之前,所有项目都会被解析并传递。
有人可以帮忙吗?
安德烈亚斯
答案 0 :(得分:3)
你确定这不是异步发生的吗?您是根据您在UI中感知的内容假设这个,还是设置了断点并确定事实上是这样的?
请注意,WPF的Dispatcher
使用优先级队列,DispatcherScheduler
调度具有Normal
优先级的项目,这优先于用于输入,布局和呈现的优先级。如果结果足够快,那么在处理完最后一个结果之后,UI可能不会更新:调度程序可能太忙于处理结果以执行UI的布局和渲染。
您可以尝试覆盖DispatcherScheduler
的行为来安排自定义优先级,如下所示:
public class PriorityDispatcherScheduler : DispatcherScheduler
{
private readonly DispatcherPriority _priority;
public PriorityDispatcherScheduler(DispatcherPriority priority)
: this(priority, Dispatcher.CurrentDispatcher) {}
public PriorityDispatcherScheduler(DispatcherPriority priority, Dispatcher dispatcher)
: base(dispatcher)
{
_priority = priority;
}
public override IDisposable Schedule<TState>(TState state, Func<IScheduler, TState, IDisposable> action)
{
if (action == null)
throw new ArgumentNullException("action");
var d = new SingleAssignmentDisposable();
this.Dispatcher.BeginInvoke(
_priority,
(Action)(() =>
{
if (d.IsDisposed)
return;
d.Disposable = action(this, state);
}));
return d;
}
}
然后将ObserveOnDispatcher()
替换为ObserveOn(new PriorityDispatcherScheduler(p))
来修改您的可观察序列,其中p
是适当的优先级(例如Background
)。
,这看起来非常可疑:ToObservable(NewThreadScheduler.Default)
。我相信这会导致每次进入结果时都会创建一个新线程,其唯一目的是将其传递给调度程序,之后新线程将终止。这几乎肯定不是你想要的。我假设您只是希望在单独的线程上处理该文件;如上所述,如果您的IEnumerable
产生1,000个项目,那么您的代码最终会创建1,000个短期线程,其中没有一个实际上正在执行读取文件的工作。
最后,是否在调度程序线程上调用了OpenFile()
?如果是这样,我相信将要发生的事情如下:
Subscribe()
,它将处理可观察操作符链,一直回到ParseDataObservable(file)
。IEnumerable
序列,将每个结果触发到ToObservable()
创建的可观察序列中。如果是这种情况,则在任何结果传递给AddIhvToList()
之前,整个文件将被读取,因为调度程序被绑定读取文件而不会绕过处理队列中的结果,直到它完成。如果发生这种情况,您可以尝试按如下方式更改代码:
var obs = _parser.ParseDataObservable(file)
.ToObservable()
.SubscribeOn(/*NewThread*/Scheduler.Default)
.ObserveOnDispatcher() // consider using PriorityDispatcherScheduler
.Subscribe<Ihv>(AddIhvToList, ReportError, ReportComplete);
注入SubscribeOn()
应确保IEnumerable
的迭代(即文件的读取)发生在单独的线程上。 Scheduler.Default
应该足够了,但如果你真的需要(你可能不需要),你可以使用NewThreadScheduler
。调度程序线程将在设置完所有内容后从Subscribe()
返回,释放它以继续处理其队列,即将结果传递给AddIhvToList()
。这应该为您提供异步行为你渴望。