簡體   English   中英

Reactive Extensions:如何觀察IEnumerable方法結果異步

[英]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() 如果是這樣,我相信將要發生的事情如下:

  1. Dispatcher(在UI線程上)將調用Subscribe() ,它將處理可觀察操作符鏈,一直回到ParseDataObservable(file)
  2. Dispatcher將遍歷您的IEnumerable序列,將每個結果觸發到ToObservable()創建的可觀察序列中。
  3. 傳遞到可觀察序列的每個結果將被安排在調度程序( 當前正在運行的同一個調度程序)上傳遞。

如果是這種情況,則在將任何結果傳遞給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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM