1

ビジネス オブジェクトの 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 に結果が表示される前に、すべての項目が解析されて配信されます。

誰でも助けることができますか?

アンドレアス

4

1 に答える 1

3

これが非同期で起こっていないことは確かですか? UI で認識したことに基づいてこれを想定していますか、それともブレークポイントを設定して、実際にそうであると判断しましたか?

WPFDispatcherは優先度キューを使用し、優先度のあるDispatcherScheduler項目をスケジュールすることに注意してくださいNormal。これは、入力、レイアウト、およびレンダリングに使用される優先度レベルよりも優先されます。結果がすぐに返ってくると、最後の結果が処理されるまで UI が更新されない可能性があります。ディスパッチャが結果の処理で忙しすぎて、UI のレイアウトとレンダリングを実行できない可能性があります。

DispatcherScheduler次のように、カスタム優先度で toの動作をオーバーライドしてみてください。

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). これにより、結果をディスパッチャーに渡すためだけに、結果が来るたびに新しいスレッドが作成され、その後新しいスレッドが終了すると思います。これは、ほぼ確実に意図したものではありません。別のスレッドでファイルを処理したかっただけだと思います。書かれているように、コードが 1,000 個のアイテムを生成する場合、コードは文字通り 1,000 個の短命のスレッドを作成するIEnumerableことになり、実際にファイルを読み取る作業を行うスレッドはありません。

最後OpenFile()、ディスパッチャ スレッドで呼び出されていますか? もしそうなら、私は次のようになると信じています。

  1. ディスパッチャ (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本当に必要な場合は a を使用できます (おそらく必要ありません)。ディスパッチャ スレッドはSubscribe()、すべてが設定された後に戻り、解放してキューの処理を続行します。つまり、結果がAddIhvToList()入ってくると に渡します。これにより、希望する非同期動作が得られるはずです。

于 2013-10-25T15:41:28.950 に答える