Wpis z mikrobloga

#csharp #programowanie

Jak ja uwielbiam te Microsoftowe pomysły niekiedy. W WPF mieliśmy coś takiego jak SynchronizationContext, ale jak się używało Dispatchera, to dało się omijać wszystkie problemy, bo Dispatcher miał BeginInvoke - do wywołań asynchronicznych oraz mieliśmy Invoke - do wymuszenia na jakimś asyncu aby wykonał się synchronicznie.

W WPF to bym w ogóle nie próbował tego jakkolwiek asynchronicznie robić, bo przecież wszystkie operacje byłyby tam synchroniczne (miałyby wersje asynchroniczne które mnie nie interesowały). No ale M$ MUSIAŁ #!$%@?ć asynci we wszystkie klasy i metody listujące foldery (PRZECIEŻ TO NIEMOŻLIWE ABY KTOŚ CHCIAŁ ODCZYTAĆ PLIK Z BLOKADĄ UI), dyski zewnętrzne, odczytujące zawartość plików itd. No to poprzez przymus cała funkcja i wszystkie jej flaki musiały być na async/await.

No i dzisiaj spędziłem ładnych kilka godzin próbując uzyskać efekt synchronicznego wywołania... Nie spodziewałem się jeszcze do dzisiaj, że kiedyś stanę przed wyzwaniem aby zrobić coś synchronicznie a nie asynchronicznie. No ale dobra, ciśniemy.

Dispatcher.Invoke? Nie, #!$%@? - nie ma takiego synchronicznego wywołania, masz tylko Dispatcher.RunAsync i #!$%@?. Oczywiście RunAsync też jest asynciem, więc wypadałoby go awaitować, skoro chodzi mi o uzyskanie efektu synchronicznego, no ale funkcja była nadpisana i synchroniczna, więc się nie da. No to co, trzeba robić jakieś brudackie Task.WaitAll() jak zwierzę. #!$%@? #!$%@?, deadlock bo nie łapie SynchronizationContext. Hmm, było na to obejście hmm... A no tak, wszystkie funkcje z await trzeba było zrobić .ToTask().ConfigureAwait(false) żeby nie próbował wrócić do kontekstu podczas postojów. No super, zaczęło działać. No ale pod spodem były wyjątki i oczywiście MessageDialog aby użytkownik miał jakiś feedback (który oczywiście też MUSI być tylko i wyłącznie async). #!$%@? wyjątek, że nie mogę spoza wątku UI pokazywać żadnych popupów, bo nie. Na Stacku ludzie piszą aby użyć do tego Dispatchera podpierdzielonego jakoś statycznie ale jak to zrobiłem, to znowu zaczął gubić kontekst i zaczęły się deadlocki na wyjątkach.

Ostatecznie musiałem kompletnie zrezygnować z obsługi wyjątków w tej funkcji i zadowolić się jedynie synchronicznym wywołaniem przebiegu udanego a wyjątki łapać już w wątku UI i tam pokazywać MessageDialog bo inaczej nie działa. Kod wygląda jak gówno a sparsowanie XMLa zajęło z 2h czasu tylko dlatego, że chciałem aby aplikacja na chwilę przy tym stanęła w miejscu, bo to była konfiguracja dla tej aplikacji.
  • 11
@Saly: Bo nie mogłem. To się miało wykonać w trakcie ładowania aplikacji a UI ładowało się nieco szybciej (tam były zawarte opcje także dla tego UI), więc byłby kłopot. Ponadto odpalało się to w App.cs w funkcji OnLoaded, która jest synchroniczna. I w WPF nie byłoby żadnego problemu.
@Khaine generalnie zasada jest taka, że jak próbujesz zrobić coś prostego i nie da się tego łatwo zrobić to w 90% przypadków używasz frameworka niezgodnie z zamysłem autora. Inaczej mówiąc wbijasz gwóźdź śrubokrętem :)
@Maav: Próbowałem GetAwaiterem. Deadlock był chyba. Co śmieszne, nawet AsTask().Wait() odpalone na Dispatcher.RunAsync() kończyło się deadlockiem XD To już chyba bug jakiś po prostu, bo żeby Dispatcher nie zwrócił do głównego wątku? Ewentualnie zakleszczył się na samym .Wait().

@Yahoo_: No to M$ mogłoby dać płaski i krzyżakowy śrubokręt jak w WPF, a nie rzucić w pysk samym krzyżakiem i powiedzieć, że jak będziesz miał płaską śrubę to sobie najwyżej wykręcisz
@Khaine sęk w tym, że to Ty sobie wybrałeś tę płaską śrubę i teraz się męczysz z krzyżakowym śrubokrętem :). Szukałeś może jakichś wzorców używa się, żeby rozwiązać Twój problem? Wiele razy widziałem jak ludzie na siłę próbowali używać wzorców z innego frameworka i wychodziło gówno, a sam framework oferował analogiczną funkcje, w prosty do implementacji sposób, ale nieco inny niż przyzwyczajenie programisty.
@Yahoo_: Wszystkie odpowiedzi na stacku mnie tak prowadziły. Generalnie UWP nie jest zbyt popularne, więc nie jest zbyt łatwo rozwiązywać takie problemy. Tak naprawdę doświadczenie z WPF mnie uratowało, bo bez ConfigureAwait(false) bym się zesrał już kompletnie. W momencie gdy Dispatcher nie zezwalał na synchroniczne wywołania ani nawet na oczekiwanie w nim na koniec wywołania asynchronicznego to już zaczęło się robić źle.
@Khaine: znaczy zależy czego szukałeś :P. Bo jak szukałeś "how to load data sync UWP" to nie dziwota, że rozwiązania były pokraczne (bo UWP nie bardzo to wspiera z tego co kojarzę). Jakbyś poszukał "how to load configuration before start UWP" (czy co tam próbowałeś zrobić) to może znalazłbyś jakiś ładny wzorzec jak to się robi w UWP. Generalnie jeśli nie masz bardzo specyficznego przypadku to przypuszczam, że autorzy mimo wszystko
Ta robota strasznie ciebie męczy albo jakoś strasznie lubisz narzekać :-)


@ZapomnialWieprzJakProsiakiemByl: E tam, ja to lubię generalnie, ale M$ ma niekiedy dziwne drogi którymi podąża. Jak się nabierze wprawy bo się zobaczy co ten inżynier M$ miał na myśli w momencie jak to tworzył i jaką furtkę na coś przewidział, to wtedy idzie jak złoto. Problem w tym, że nie zawsze tak łatwo znaleźć tą "słuszną drogę".

@Yahoo_: Teraz
@Yahoo_: Porzeźbiłem dzisiaj jeszcze. Podtrzymuję swój hejt a nawet wzmacniam go. Win 10 IOT zachowuje się inaczej niż środowisko UWP które działa na Win 10. Deadlocki nie występują na desktopie, ale jak wrzucisz na Raspberry to nagle zaczynają się pojawiać. Wyjebiste, dokładnie w takich warunkach chcesz wepchnąć użytkownikowi siłą asynci w mordę. Niezależnie od tego co zrobię, na Win 10 IOT zawsze zgubi kontekst (w tym konkretnym przykładzie). Na kompie nie