2009-06-29 7 views
30

Mam aplikację pulpitu C#, w której jeden wątek tworzony przeze mnie stale pobiera obraz ze źródła (faktycznie jest to aparat cyfrowy) i umieszcza go na panelu (panel. image = img) w GUI (który musi być inny wątek, ponieważ jest to kod z opóźnieniem z kontrolą.InvalidOperationException - obiekt jest obecnie w użyciu w innym miejscu - czerwony krzyż

prace aplikacji, ale na niektórych maszynach pojawia się następujący błąd w losowych odstępach czasu (nieprzewidywalne)

************** Exception Text ************** 
System.InvalidOperationException: The object is currently in use elsewhere. 

Następnie panel zamienia się w czerwony krzyż, czerwony X - uważam, że jest to nieprawidłowa ikona obrazu, którą można edytować z właściwości. Jon działa, ale panel nigdy nie jest aktualizowany.

Z tego co wiem, ten błąd pochodzi z zdarzenia kontrolnego, w którym narysowałem coś innego na obrazie.

Próbowałem za pomocą zamka tam, ale nie szczęścia :(

Sposób I wywołać funkcję, która stawia obraz na panelu jest następujące:

if (this.ReceivedFrame != null) 
{ 
    Delegate[] clients = this.ReceivedFrame.GetInvocationList(); 
    foreach (Delegate del in clients) 
    { 
     try 
     { 
      del.DynamicInvoke(new object[] { this, 
       new StreamEventArgs(frame)}); 
     } 
     catch { } 
    } 
} 

to delegat:

public delegate void ReceivedFrameEventHandler(object sender, StreamEventArgs e); 
    public event ReceivedFrameEventHandler ReceivedFrame; 

i to, w jaki sposób funkcja wewnątrz kontroli kodu źródłowego rejestruje do niego:

Camera.ReceivedFrame += 
    new Camera.ReceivedFrameEventHandler(camera_ReceivedFrame); 

Próbowałem też

del.Method.Invoke(del.Target, new object[] { this, new StreamEventArgs(b) }); 

zamiast

del.DynamicInvoke(new object[] { this, new StreamEventArgs(frame) }); 

ale nie szczęścia

Czy ktoś wie jak mogę naprawić ten błąd lub przynajmniej złapać błąd jakoś i zrobić nić ponownie umieścić obrazy na panelu?

Odpowiedz

0

myślę, że to jest wielowątkowość problemem używać Windows złota zasada i zaktualizuj panel w głównym użycia nici panel.Invoke To powinno rozwiązać przekrój wątków wyjątek

+0

Aktualizuję panel w głównym wątku, ale wywołuję funkcję, która aktualizuje go z innego wątku i przekazuje obraz jako parametr. –

+0

jeśli wywołasz funkcję, która aktualizuje panel z innego wątku i samej funkcji, nie ma przełączania kontekstu (przełącz się na główny wątek, używając na przykład invoke) oznacza to, że twoja aktualizacja została wykonana na innym wątku, a nie głównym wątku –

18

To dlatego GDI + klasa Obraz nie jest bezpieczeństwo wątków. Hovewer można uniknąć InvalidOperationException za pomocą zamka za każdym razem, gdy trzeba dostępem obrazu, na przykład do malowania lub uzyskanie rozmiaru obrazu:

Image DummyImage; 

// Paint 
lock (DummyImage) 
    e.Graphics.DrawImage(DummyImage, 10, 10); 

// Access Image properties 
Size ImageSize; 
lock (DummyImage) 
    ImageSize = DummyImage.Size; 

BTW, inwokacja nie jest potrzebna, jeśli będziesz korzystać powyższy wzór.

+0

funkcja, która działa wewnątrz zdarzenia onpaint, rysuje wiele rzeczy na panelu nad obrazem, który inny wątek ustawia na panelu, więc w jaki sposób mogę zablokować ciągnący się everithing? obejmuje to prostokąty, linie i obrazy –

+0

Związałem blokowanie panelu, ale to nie zadziałało. Nadal mam błąd. –

+0

Nie musisz blokować panelu, musisz zablokować konkretny obraz, z którym pracujesz. – arbiter

2

Wydaje mi się, że ten sam obiekt z kamery jest używany kilka razy.

E.g. spróbuj użyć nowego bufora dla każdej odebranej ramki. Wydaje mi się, że gdy ramka graficzna rysuje nową ramkę, biblioteka przechwytywania ponownie wypełnia ten bufor. Dlatego na szybszych komputerach może to nie stanowić problemu, a na wolniejszych komputerach może to stanowić problem.

Zaprogramowałem coś podobnego raz, po każdej odebranej ramce, musieliśmy poprosić o odebranie następnej klatki i ustawić bufor odbioru ramki w tym żądaniu.

Jeśli nie można tego zrobić, skopiuj odebranej ramki z aparatu najpierw do nowego bufora i załącza bufor do kolejki, albo po prostu użyć 2 bufory przemienne i sprawdzić przekroczenia. Użyj funkcji myOutPutPanel.BeginInvoke, aby wywołać metodę camera_ReceivedFrame, lub lepiej, aby uruchomiono wątek, który sprawdza kolejkę, gdy ma nowy wpis, wywołuje mnyOutPutPanel.BeginInvoke w celu wywołania metody, aby ustawić nowy bufor jako obraz na panelu.

Ponadto, po otrzymaniu bufora, użyj metody wywołania panelu, aby wywołać ustawienie obrazu (zagwarantowanie, że działa w wątku okna, a nie wątku z biblioteki przechwytywania).

Poniższy przykład można wywołać z dowolnego wątku (biblioteka przechwytywania lub innym osobnym wątku):

void camera_ReceivedFrame(object sender, StreamEventArgs e) 
{ 
    if(myOutputPanel.InvokeRequired) 
    { 
     myOutPutPanel.BeginInvoke( 
      new Camera.ReceivedFrameEventHandler(camera_ReceivedFrame), 
      sender, 
      e); 
    } 
    else 
    { 
     myOutPutPanel.Image = e.Image; 
    } 
} 
4

miałem podobny problem z tym samym komunikatem o błędzie, ale spróbuj jak ja może, blokowanie bitmapy nie zrobił” Naprawię wszystko dla mnie. Wtedy zdałem sobie sprawę, że rysuję kształt za pomocą statycznego pędzla. Rzeczywiście, to właśnie pędzel spowodował rywalizację o wątki.

var location = new Rectangle(100, 100, 500, 500); 
var brush = MyClass.RED_BRUSH; 
lock(brush) 
    e.Graphics.FillRectangle(brush, location); 

To zadziałało dla mojego przypadku i lekcji: Sprawdź wszystkie typy referencji używane w miejscu, w którym występuje rywalizacja o wątki.

+1

W moim przypadku był to również pędzel. Dla zabawy wypróbowałem go czcionką, ale wydaje się, że działa z wielu wątków. –

Powiązane problemy