Dziwny błąd i problem w rozwiązaniu

0

Błąd powstaje w warunkach częstych zmian i częstych wyświetleń map bitowych.

Fragment kodu wywoływany klawiaturą (można trzymać wciśnięty klawisz, wtedy dzieje się to z dużą częstotliwością):

                        if (LcdChangeAllowed)
                        {
                            try
                            {
                                LcdDispAllowed = false;

                                int LCDCustomChar = LCDAddr >> 3;
                                int LCDCustomLine = LCDAddr & 7;

                                bool D;
                                bool[] LCDLineBits = new bool[5];

                                IntToBin(LCDWriteData, out D, out D, out D, out LCDLineBits[0], out LCDLineBits[1], out LCDLineBits[2], out LCDLineBits[3], out LCDLineBits[4]);

                                for (int i = 0; i < 5; i++)
                                {
                                    if (LCDLineBits[i])
                                    {
                                        DrawLCDPixel(0, LCDCustomChar, i, LCDCustomLine, Color.FromArgb(0, 0, 0));
                                        DrawLCDPixel(1, LCDCustomChar, i, LCDCustomLine, Color.FromArgb(255, 255, 255));
                                        if (LCDCustomLine < 7)
                                        {
                                            DrawLCDPixel(2, LCDCustomChar, i, LCDCustomLine, Color.FromArgb(0, 0, 0));
                                            DrawLCDPixel(3, LCDCustomChar, i, LCDCustomLine, Color.FromArgb(255, 255, 255));
                                        }
                                        else
                                        {
                                            DrawLCDPixel(2, LCDCustomChar, i, LCDCustomLine, Color.FromArgb(0, 0, 0));
                                            DrawLCDPixel(3, LCDCustomChar, i, LCDCustomLine, Color.FromArgb(0, 0, 0));
                                        }
                                    }
                                    else
                                    {
                                        DrawLCDPixel(0, LCDCustomChar, i, LCDCustomLine, Color.FromArgb(255, 255, 255));
                                        DrawLCDPixel(1, LCDCustomChar, i, LCDCustomLine, Color.FromArgb(128, 128, 128));
                                        if (LCDCustomLine < 7)
                                        {
                                            DrawLCDPixel(2, LCDCustomChar, i, LCDCustomLine, Color.FromArgb(255, 255, 255));
                                            DrawLCDPixel(3, LCDCustomChar, i, LCDCustomLine, Color.FromArgb(128, 128, 128));
                                        }
                                        else
                                        {
                                            DrawLCDPixel(2, LCDCustomChar, i, LCDCustomLine, Color.FromArgb(0, 0, 0));
                                            DrawLCDPixel(3, LCDCustomChar, i, LCDCustomLine, Color.FromArgb(0, 0, 0));
                                        }
                                    }
                                }
                            }
                            catch
                            {
                                MessageBox.Show("Zmiana");
                            }
                            LcdDispAllowed = true;
                        }

Treść metody DrawLcdPixel, LCDDisp to tablica bitmap

        private void DrawLCDPixel(int Nr, int Chr, int X, int Y, Color Clr)
        {
            bool NotDone = true;
            while(NotDone)
            {
                try
                {
                    LCDDisp[Nr, Chr].SetPixel(X * 2 + 0, Y * 2 + 0, Clr);
                    LCDDisp[Nr, Chr].SetPixel(X * 2 + 1, Y * 2 + 0, Clr);
                    LCDDisp[Nr, Chr].SetPixel(X * 2 + 0, Y * 2 + 1, Clr);
                    LCDDisp[Nr, Chr].SetPixel(X * 2 + 1, Y * 2 + 1, Clr);
                    LCDDisp[Nr, Chr + 8].SetPixel(X * 2 + 0, Y * 2 + 0, Clr);
                    LCDDisp[Nr, Chr + 8].SetPixel(X * 2 + 1, Y * 2 + 0, Clr);
                    LCDDisp[Nr, Chr + 8].SetPixel(X * 2 + 0, Y * 2 + 1, Clr);
                    LCDDisp[Nr, Chr + 8].SetPixel(X * 2 + 1, Y * 2 + 1, Clr);
                    NotDone = false;
                }
                catch
                {
                    NotDone = true;
                    MessageBox.Show("DrawLCDPixel");
                }
            }
        }

Zmienne LcdChangeAllowed i LcdDispAllowed są typu bool i mają zapewnić uniknięcie próby wykonywania obu tych fragmentów naraz

Drugi fragment, wywoływany z timera, im mniejszy interwał timera tym większe prawdopodobnieństwo błędu.

                        if (LcdDispAllowed)
                        {
                            LcdChangeAllowed = false;

                            try
                            {
                                LCD00.Image = LCDDisp[L0Cur[0], L0Disp[0]];
                                LCD01.Image = LCDDisp[L0Cur[1], L0Disp[1]];
                                LCD02.Image = LCDDisp[L0Cur[2], L0Disp[2]];
                                LCD03.Image = LCDDisp[L0Cur[3], L0Disp[3]];
                                LCD04.Image = LCDDisp[L0Cur[4], L0Disp[4]];
                                LCD05.Image = LCDDisp[L0Cur[5], L0Disp[5]];
                                LCD06.Image = LCDDisp[L0Cur[6], L0Disp[6]];
                                LCD07.Image = LCDDisp[L0Cur[7], L0Disp[7]];
                                LCD08.Image = LCDDisp[L0Cur[8], L0Disp[8]];
                                LCD09.Image = LCDDisp[L0Cur[9], L0Disp[9]];
                                LCD0A.Image = LCDDisp[L0Cur[10], L0Disp[10]];
                                LCD0B.Image = LCDDisp[L0Cur[11], L0Disp[11]];
                                LCD0C.Image = LCDDisp[L0Cur[12], L0Disp[12]];
                                LCD0D.Image = LCDDisp[L0Cur[13], L0Disp[13]];
                                LCD0E.Image = LCDDisp[L0Cur[14], L0Disp[14]];
                                LCD0F.Image = LCDDisp[L0Cur[15], L0Disp[15]];

                                LCD10.Image = LCDDisp[L1Cur[0], L1Disp[0]];
                                LCD11.Image = LCDDisp[L1Cur[1], L1Disp[1]];
                                LCD12.Image = LCDDisp[L1Cur[2], L1Disp[2]];
                                LCD13.Image = LCDDisp[L1Cur[3], L1Disp[3]];
                                LCD14.Image = LCDDisp[L1Cur[4], L1Disp[4]];
                                LCD15.Image = LCDDisp[L1Cur[5], L1Disp[5]];
                                LCD16.Image = LCDDisp[L1Cur[6], L1Disp[6]];
                                LCD17.Image = LCDDisp[L1Cur[7], L1Disp[7]];
                                LCD18.Image = LCDDisp[L1Cur[8], L1Disp[8]];
                                LCD19.Image = LCDDisp[L1Cur[9], L1Disp[9]];
                                LCD1A.Image = LCDDisp[L1Cur[10], L1Disp[10]];
                                LCD1B.Image = LCDDisp[L1Cur[11], L1Disp[11]];
                                LCD1C.Image = LCDDisp[L1Cur[12], L1Disp[12]];
                                LCD1D.Image = LCDDisp[L1Cur[13], L1Disp[13]];
                                LCD1E.Image = LCDDisp[L1Cur[14], L1Disp[14]];
                                LCD1F.Image = LCDDisp[L1Cur[15], L1Disp[15]];
                            }
                            catch
                            {
                                MessageBox.Show("Wyswietlanie");
                            }

                            LcdChangeAllowed = true;
                        }

Według kodu, wszystko wydaje sie być w porządku, jest zabezpieczenie przed próbą jednoczesnego wykonania (zmienne LcdChangeAllowed i LcdDispAllowed), a także jest zapewniona obsługa błędu poprzez wyświetlenie komunikatu z odpowiednią (try/catch, w catch jest MessageBox.Show).

Jak widać, wszystko jest w porządku, ale po odpaleniu i wystąpieniu błędu nie pojawia się żaden z komunikatów ShowMessage, a pojawia się taki ekran:

user image

Jeżeli jeden z przedstawionych fragmentów kodu wyłączę z działania poprzez zamianę na komentarz, to obojętnie, który z tych dwóch fragmentów wyłączę, wtedy błędu nie ma, tylko, że oba fragmenty są potrzebne.

Normalnie, program jest wielowątkowy, ale przewidziałem w nim wyłączenie wielowątkowości. Obie przedstawione metody wykonują się w osobnych wątkach. Jeżeli jest ta wielowątkowość, to wymieniony problem występuje. A jeżeli jest jednowątkowość (bez wielowątkowości), to problemu nie ma.

Z tego, co zauważyłem, w całej tej sprawie chodzi o dostęp do tablicy LCDDisp przed dwie metody naraz.
Tylko śmieszne jest to, że tylko jeden z tych dwóch fragmentów modyfikuje tą tablicę, a drugi z nich tylko ją czyta.

W przypadku pierwszego fragmentu (ten, który zapisuje do tablicy LCDDisp), ten kod MUSI być wykonany, a jeżeli LCDDisp jest zablokowany i kod nie może być wykonany, to program ma poczekać, aż LCDDisp się zwolni i wykonać kod.

Co do drugiego fragmentu, w którym jest tylko czytanie LCDDisp, to jeżeli czytania nie da się zrealizować, to wykonywanie kodu może zostać pominięte, a może być wykonany później, w tym przypadku to bez różnicy.

Gdzie szukać przyczyny i jak rozwiązać problem?

0

komunikat wyjatku jest dosc jasny, dwa lub wiecej watkow probuja uzywac tego samego obiektu

musisz dodac synchronizacje dostepu do bitmap
uzyj lock, albo semaforow
oraz nie zapominaj o zwalnianiu obiektow gdi

0
massther napisał(a)

komunikat wyjatku jest dosc jasny, dwa lub wiecej watkow probuja uzywac tego samego obiektu

Właśnie nie zbyt jasny. Komunikat sugeruje, ze chodzi o dostęp do obiektu DSM51For, czyli cała forma programu. Z niego nie wynika, przy wykonywaniu której instrukcji ten błąd wystąpił.

To, że chodzi o bitmapy, to ja sam z dużym trudem doszedłem.

Dziwne jest to, ze mimo objęcia instrukcji konstrukcją try/catch, ta konstrukcja jest ignorowana i jest tak, jakby nie było try/catch.

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