Dispatcher i wątki

0

Witam,
Mam program w którym postać porusza się po planszy wg. algorytmu Dijkstry. Stworzyłem wątek w którym postać przechodzi punkt po punkcie pobierając koordynaty z listy, do poruszania się służy metoda Move która pobiera aktualną pozycję oraz pozycję na której ma się znaleźć postać, z racji iż odwołuje się do elementów mojego głównego okienka WPF'a użyłem dispatchera:

                foreach (Point point in way)
                {
                    Application.Current.Dispatcher.BeginInvoke(
                        DispatcherPriority.Background,
                        new Action(() => controls.Move(prevPointX, prevPointY, (int) point.X, (int) point.Y)));

                    prevPointX = (int)point.X;
                    prevPointY = (int)point.Y;

                    Thread.Sleep(100);
                }

Jednak poruszanie postaci nie jest odpowiednie, czasem nic się nie dzieje, czasem pojawia się w kilku miejscach naraz, jednak wiem że funkcja Move działa poprawnie i poprawne parametry są przekazywane. Co może powodować takie zachowanie? Oraz jak mu zapobiec?

1

@Carlj28

Nie znam całego kodu i raczej mam za mało danych aby stwierdzić w czym dokładnie problem ale patrząc na to co zamieściłeś rzuca się w oczy jedna rzecz. Twój kod w najlepszym przypadku będzie zachowywał się nieprzewidywalnie. Podczas kolejnych iteracji pętli tworzysz metody anonimowe i przechwytujesz w nich zmienne zewnętrzne których zasięg wykracza poza aktualną iterację pętli. Nie wiesz jednak tego, że podczas przechwycenia zmienne te są opakowywane i przenoszone na stertę. Ponieważ zmienne te zostały utworzone poza iteracją pętli dlatego też wszystkie metody anonimowe które tworzysz będą współdzieliły te same zmienne. W momencie wykonania metody anonimowej nie można przewidzieć jaką te zmienne będą miały wartość gdyż mogą zostać zmodyfikowane poza zakresem (i tak się też dzieje gdyż w każdej iteracji przypisujesz im nową wartość). Dodaj jeszcze do tego asynchroniczne wywołanie Dispatchera i Thread.Sleep() i problem gotowy.

Przede wszystkim więc utwórz zmienne lokalne wewnątrz pętli przed utworzeniem metody anonimowej i skopiuj do nich potrzebne wartości:

var prevPointCopyX = prevPointX;
var prevPointCopyY = prevPointY;
var actPointX = (int) point.X;
var actPointY = (int) point.Y;

A następnie utwórz metodę anonimową wykorzystując skopiowane do lokalnych zmiennych wartości:

() => controls.Move(prevPointCopyX, prevPointCopyY, actPointX, actPointY))

Nie wiem czy to pomoże ale na pewno wyeliminuje potencjalną przyczynę innych błędów.

0

Dwie rzeczy mi się nasuwają :

  • w pętli wywołujesz 100 razy dispatchera ( domyślam się, że 10x10 imageboxów ) . Zrób z tego jedno wywołanie dispatchera i oblicz za jednym razem wszystkie punkty. Bo z dispatcherem jest tak, że zostanie on wywołany wtedy i tylko wtedy gdy główny wątek tak sobie zażyczy (podobnie jak z GC). Nie mamy na to wpływu.
    Zrób w metodzie zmienne lokalne , skopiuj do nich dane z głównego wątku . Propertsy i zmienne nie są thread safe ! ( jest jeden wyjątek ale na tym typie możesz tylko dodawać i odejmować ). Doczytaj o operacjach atomowych.
    A debug pewnie pokazuje ci wartości zmiennych widocznych z wątku głównego a nie z dodatkowego wątku.
    Lektura http://www.pzielinski.com/?p=176

  • problem z odświerzaniem grafiki ? Double Buffered dla kontenera włącz. Ale osobiście stawiam na punkt wyżej.

1 użytkowników online, w tym zalogowanych: 0, gości: 1