2015-11-13 6 views
5

zauważyłem następujące zachowanie. Komunikaty wyjściowe konsoli pojawiają się w niewłaściwym folderze, gdy są zapełniane przez IProgress.
komunikaty pojawiające się na konsole w niewłaściwej kolejności, gdy raportowanie postępów z IProgress.Report()

var recounter = new IdRecounter(filePath, new Progress<string>(Console.WriteLine)); 
       recounter.RecalculateIds(); 

staram się poprawić moje hermetyzacji, ponownego użycia i projektowe umiejętności. Mam więc klasę o nazwie IdRecounter. Chcę go teraz używać w aplikacji konsolowej, ale później prawdopodobnie w aplikacji WPF lub czymkolwiek innym.

Dlatego chcę, aby klasa była całkowicie nieświadoma jego środowiska - ale jednocześnie chcę zgłosić postęp akcji "na żywo" - dlatego używam typu IProgress, który pozwoli mi umieścić rzeczy do konsoli, do pliku dziennika lub zaktualizować właściwość etykiety stanu itp. (proszę dać mi znać, jeśli nie tak to robisz)

Zauważyłem, że ma tendencję do przesyłania wiadomości do konsoli w nieprawidłowy sposób zamówienie, np tworzenie plików 1
tworzenie pliku 4
tworzenie pliku 5
tworzenie pliku 3
Wszystko gotowe!
tworzenie pliku 2

Kiedy przeszedłem IProgress (MyProgress.Report()) z Console.WriteLine() to działa zgodnie z oczekiwaniami.
Co jest przyczyną tego i jak można ją kontrolować?

Dzięki

+0

To, jak wygląda "Postęp" na pewno pomogłoby, gdyby pokazać cały odpowiedni kod. Nie można powiedzieć "Jack" na podstawie tego, co napisałeś. – MethodMan

+0

Prawdopodobny duplikat synchronizacji [IProgress ] (https: // stackoverflow. com/q/17982555) –

Odpowiedz

7

Klasa Progress<T> wykorzystuje bieżący kontekst synchronizacji dla wątku, w którym został stworzony, aby odwołać się do procedury obsługi zdarzeń dla jego ProgressChanged imprezy.

W aplikacji konsolowej domyślny kontekst synchronizacji wykorzystuje pulę wątków do wywoływania delegatów, zamiast przywracania ich do wątku, w którym kontekst jest pobierany. Oznacza to, że za każdym razem, gdy aktualizujesz postęp, procedura obsługi zdarzenia może zostać wywołana w innym wątku (szczególnie jeśli aktualizacje postępu następują szybko po sobie).

Ze względu na sposób zaplanowania wątków, nie ma gwarancji, że pracownik puli wątków przypisał zadanie, zanim inny pracownik puli wątków faktycznie wykona zadanie, zanim inny robotnik wykona swoje zadanie. Specjalnie dla stosunkowo prostych zadań (takich jak emitujących wiadomości toku), może łatwo być przypadek wówczas, że zadania skolejkowany później faktycznie zakończone przed zadaniami skolejkowany wcześniej.

Jeśli chcesz do wiadomości aktualizacyjne Postępy być zagwarantowane mają być wyświetlane w kolejności, trzeba użyć innego mechanizmu. Można na przykład skonfigurować producenta/konsumenta pod numerem BlockingCollection<T>, w którym są wiadomości zużywające pojedynczy wątek, które są kolejkowane (tworzone) przez operacje raportujące postęp. Lub, oczywiście, możesz po prostu zadzwonić bezpośrednio pod numer Console.WriteLine() (co już sprawdziłeś, zadziała).

Należy pamiętać, że to nie znaczy, trzeba porzucić pomysł wykorzystania IProgress<T>. To po prostu oznacza, że ​​musisz podać swoją realizację, zamiast przy użyciu klasy Progress<T>, przynajmniej w scenariuszu konsoli.Na przykład:

class ConsoleProgress : IProgress<string> 
{ 
    public void ReportProgress(string text) 
    { 
     Console.WriteLine(text); 
    } 
} 

Pozwoliłoby to, na przykład, zachować IProgress<T> abstrakcję w klasie IdRecounter(), oddzielenie tego typu z kontekstu UI. To może być ponownie użyty do programu konsoli, jak również dowolnego programu GUI API, takich jak WinForm, WPF, Winrt itp


Podsumowując: Progress<T> jest bardzo przydatnym realizacja IProgress<T> kiedy trzeba Streszczenie wątek krzyżowy, operacje związane z kontekstem synchronizacji, które są potrzebne w programie GUI. Będzie działał w programach konsolowych, ale ponieważ w tym przypadku będzie używał puli wątków, możesz nie uzyskać deterministycznie uporządkowanych danych wyjściowych, przynajmniej nie bez dodatkowej synchronizacji z programami obsługi zdarzenia ProgressChanged.

0

Klasa ta naśladuje podstawowe zachowanie Progress<T> dla aplikacji konsoli:

public class ConsoleProgress<T> : IProgress<T> 
{ 
    private Action<T> action; 

    public ConsoleProgress(Action<T> action) 
    { 
     this.action = action; 
    } 
    public void Report(T value) 
    { 
     action(value); 
    } 
} 

Jest tylko działanie, ale realizacja wydarzenie podobnie jak w Progress<T> byłoby zbyt proste.

Powiązane problemy