Co do FileDataServices
, odczyt z plików nie trwa długo, nie stanowi to najmniejszego problemu. Rozważałem asynchroniczny zapis, ale ostatecznie zdecydowałem się zostać przy synchronicznym. Czy to jakiś problem?
No nie trwa zbyt długo i w aplikacji desktopowej to raczej nie ma dużego znaczenia, ale dla przyzwoitości można napisać tak jak poprawnie powinno być :P
W apce webowej teoretycznie mogłoby to mieć znaczenie, bo tam możesz dostać nagle 10k requestów na sekundę i przy synchronicznym zapisie mogą się skończyć dostępne wątki.
Choć może dla dobrej praktyki zrobię asynchroniczny odczyt/zapis. Choć Newtonsoft.Json
nie wspiera asynchronicznej (de)serializacji.
A nie możesz serializować w pamięci przez JsonConvert
, a później asynchronicznie zapisać do pliku?
Wczytałem się w ten artykuł. Jak dobrze rozumiem, konkluzja jest taka, że UI powinno dbać o nieblokowanie wątku UI i nie powinno się tworzyć nowych wątków które mogą wpłynąć na pogorszenie wydajności. Jestem skłonny się zgodzić, ale w moim przypadku Task task = Task.Run(async () =>
ma na celu właśnie utworzenie przez pętlę for
jak największej ilości Task
-ów i wątków, które będą pracować równolegle dla przyspieszenia pracy z racji długiego trwania wykonywania każdego z zadania. Mi właśnie zależy na utworzeniu wielu nowych wątków i nie znalazłem lepszego rozwiązania. Ewentualnie Parallel.For
mogłoby być alternatywą.
W poprzednim poście pisałem tylko o ScrapDataServices
i FileDataServices
, tam też w kilku miejscach wrzucasz operacje synchroniczne w Task.Run
, a nie powinno to tam być.
A co do przykładu o którym mówisz z MainViewModel
to też wcale Task.Run
tam nie jest potrzebne, bo każdą operację asynchroniczną opakowujesz w Task.Run, co znowu nie jest dobrą praktyką. Ten kod powinien wyglądać mniej więcej tak (pisane z palca, więc pewnie są jakieś błędy, ale ogólną ideę powinno być widać):
[...]
List<Task> tasks = Enumerable.Range(startIndex, stopIndex - startIndex)
.Select(i => ScrapData(i))
.ToList();
await Task.WhenAll(tasks); //albo WhenAny w pętli i tutaj aktualizujesz ProgressBara
[...]
private async Task ScrapData(int index)
{
if (dataType == "racesPl") race = await _scrapServices.ScrapSingleRacePlAsync(index);
//if the race is from 2018
if (race.RaceDate.Year == 2018)
{
lock (((ICollection)Races).SyncRoot)
{
Races.Add(race);
}
}
}
Tutaj się zgodzę w 100%. Narobiłem trochę ciężkich metod i klas, pomyślę o ich odchudzeniu i zrobieniu z niektórych metod nowych serwisów.
Ja bym zaczął od wydzielenia klasy, która będzie odpowiadała za komunikację z tym serwisem z którego scrapujesz dane (teraz ładujesz stronę przez htmlagilitypack, a z tego co widziałem on nie obsługuje asynca). Ja bym spróbował zrobić klasę, która pobierze zawartość strony jako HTML przez HttpClienta (ma asynca) i dopiero później ładował gotowego stringa do HtmlAgilityPack (chyba się da?).
Dodatkowo przydałoby się wydzielić to całe parsowanie przez HtmlAgilityPack do jakiejś oddzielnej klasy.
No i jeszcze w FileDataServices
masz kilka metod, które robią to samo (czytanie z pliku -> deserializacja, albo serializacja -> zapis do pliku). I teraz w każdej metodzie masz zrobiony copy-paste. Zrób sobie na początek jakiś generyczny helper wewnątrz tej klasy i te 3 metody GetAllHorses/GetAllJockeys/GetAllRaces to będą tylko wywołania helpera.