Frage

Ich arbeite derzeit an einer Anwendung, die aus einer großen Binärdatei liest, die mehrere tausend Dateien enthält. Jede Datei wird von einer anderen Klasse in der Anwendung verarbeitet.Diese Klasse gibt entweder ein Objekt oder null zurück.Ich möchte einen Fortschritt im Hauptformular anzeigen, komme aber aus irgendeinem Grund nicht damit klar.

int TotalFound = 0;
var uiScheduler = TaskScheduler.FromCurrentSynchronizationContext;
BufferBlock<file> buffer = new BufferBlock<file>();
DataflowBlockOptions options = new DataflowBlockOptions(){ TaskScheduler = uiScheduler, }; 
var producer = new ActionBlock<someObject>(largeFile=>{ 
    var file = GetFileFromLargeFile(largeFile);
    if(file !=null){
       TotalFound++;
       buffer.post(file);
       lblProgress.Text = String.Format("{0}", TotalFound);
    }
}, options);

Der obige Code friert mein Formular ein, auch wenn ich „TaskScheduler.FromCurrentSynchronizationContext“ verwende. Warum?Denn wenn ich den Code unten verwende, wird mein Formular problemlos aktualisiert

DataflowBlockOptions options = new DataflowBlockOptions(){ TaskScheduler = uiScheduler, }; 
var producer = new ActionBlock<someObject>(largeFile=>{ 
    var file = GetFileFromLargeFile(largeFile);
    if(file !=null){
       Task.Factory.StartNew(() => {
          TotalFound++;
          buffer.Post(file);
       }).ContinueWith(uiTask => {
          lblProgress.Text = String.Format("{0}", TotalFound);
       },CancellationToken.None, TaskContinuationOptions.None, uiScheduler);           
    }
});

Ich bin neu in diesem gesamten TPL-Datenfluss und hoffe daher, dass mir jemand etwas erklären kann, warum es im zweiten Codeausschnitt funktioniert und im ersten nicht.

Freundliche Grüße, Martijn

War es hilfreich?

Lösung

Der Grund, warum Ihre Benutzeroberfläche blockiert ist, ist weil Du verwendest FromCurrentSynchronizationContext.Dadurch wird der Code im UI-Thread ausgeführt, was bedeutet, dass er einfriert, wenn Sie eine Aktion mit langer Laufzeit ausführen (höchstwahrscheinlich). GetFileFromLargeFile()).

Auf der anderen Seite müssen Sie das ausführen lblProgress.Text im UI-Thread.

Ich bin mir nicht sicher, ob Sie das einstellen sollten lblProgress.Text Direkt in diesem Code sieht es für mich nach einer zu engen Kopplung aus.Aber wenn Sie das tun möchten, sollten Sie meiner Meinung nach genau diese Zeile im UI-Thread ausführen:

var producer = new ActionBlock<someObject>(async largeFile =>
{
    var file = GetFileFromLargeFile(largeFile);
    if (file != null)
    {
        TotalFound++;
        await buffer.SendAsync(file);
        await Task.Factory.StartNew(
            () => lblProgress.Text = String.Format("{0}", TotalFound),
            CancellationToken.None, TaskCreationOptions.None, uiScheduler);
    }
});

Aber eine noch bessere Lösung wäre, wenn Sie es machen würden GetFileFromLargeFile() asynchron und stellte sicher, dass keine lang andauernden Aktionen im UI-Thread ausgeführt werden (ConfigureAwait(false) kann dir dabei helfen).Wenn Sie das getan haben, wird der Code des ActionBlock könnte im UI-Thread ausgeführt werden, ohne dass Ihre UI einfriert:

var producer = new ActionBlock<someObject>(async largeFile =>
{
    var file = await GetFileFromLargeFile(largeFile);
    if (file != null)
    {
        TotalFound++;
        await buffer.SendAsync(file);
        lblProgress.Text = String.Format("{0}", TotalFound)
    }
}, options);
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top