[英]Reactive Extensions: How to observe a IEnumerable method result async
我有一個返回我的業務對象的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中看到結果之前,所有項目都會被解析並傳遞。
有人可以幫忙嗎?
安德烈亞斯
你確定這不是異步發生的嗎? 您是根據您在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()
時。這應該為您提供所需的異步行為。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.