2009-06-24 13 views
7

mam coś w rodzaju modelu plug-in, w którym różne skomplikowane kontrole użytkownika są przechowywane w bibliotekach DLL i załadowane i instancję w czasie wykonywania przy użyciuTworzenie kontrole non-UI wątek

Activator.CreateInstanceFrom(dllpath, classname). 

Ponieważ jestem loading kilka z nich chciałem zrobić w tle, utrzymując mój interfejs w stanie gotowości, tworząc nowy wątek do ładowania. Kontrolki są następnie nadrzędne do formularza głównego i wyświetlane w razie potrzeby.

Wydaje się to działa dobrze - dopóki nie spróbuję ustawić żadnej właściwości w zagnieżdżonej kontrolce na jednej z tych kontrolek użytkownika, np. w procedurze obsługi zdarzenia przycisku, który zgłasza wyjątek z gwintem krzyża. Zdaje sobie sprawę, że mogłem tego uniknąć, sprawdzając InvokeRequired za każdym razem, gdy uzyskuję dostęp do właściwości, ale wolałbym nie martwić się o to podczas pisania kodu dla formantów użytkownika (zwłaszcza, że ​​są inni piszący te fragmenty kodu, którzy mogą nie zawsze pamiętam).

Moje pytanie brzmi: czy istnieje bezpieczny sposób wykonania tego, co próbuję, lub jak najlepiej załadować te elementy sterujące w tle? Czy jest to w zasadzie niemożliwe i czy muszę trzymać się głównego wątku do tworzenia kontroli?

Mam nadzieję, że informacje, które podałem, są wystarczające, aby moja sytuacja była jasna; jeśli nie, chętnie opracuję i dostarczę próbki kodu.

+0

"dopóki nie spróbuję ustawić żadnej właściwości" - czy jest to w wątku, czy później? –

+0

To później, po tym jak formant użytkownika został zapisany w formularzu głównym (jak w formularzu.controls.add (mycontrol)) –

Odpowiedz

3

Dobrze jest załadować biblioteki DLL i utworzyć obiekty kontrolne w tle, ale kontrola musi zostać dodana do formularza w głównym wątku i wszystkich interakcji użytkownika, jak również każdej programowej zmiany właściwości kontrolnych (po tym, jak to się stało) stworzony) musi wystąpić w głównym wątku. Po prostu nie da się tego obejść, jak zapewne już wiesz. Teraz, chyba że ładowanie tych bibliotek DLL i tworzenie kontroli trwa wieki, nie widzę powodu, aby robić to w osobnych wątkach w tle.

Jeśli niektóre czynności wykonywane przez elementy sterujące blokują interfejs użytkownika i chcesz, aby wystąpiły w tle, jest to inna historia i lepiej przeanalizuj każde działanie indywidualnie i utwórz wyraźne metody komunikacji między wątkami interfejsu użytkownika. i wątki tła.

Próbując zrobić rodzajowe jeden odpowiadający wszystkim ramy, które działa tak samo w trybie wątku UI w trybie tła i po prostu sprawdza wyniki InvokeRequired czasami w gorzej wydajności (jak wszystkie wątki są zablokowane w Invoke) lub nawet w (niewykrytych) zakleszczeniach, gdy tylko aplikacja osiągnie rozsądną złożoność. Również robienie wszystkich aktualizacji asynchronizujących w BeginInvoke, bez uwzględnienia każdej metody osobno, może spowodować problemy z spójnością danych (tj. Kontrole mogą aktualizować się do stanów z powrotem w czasie z powodu odwrócenia kolejności wywołania między wątkami).

+0

Tak, mam wrażenie, że próbowałem być trochę zbyt sprytny. Mniej więcej porzuciłem swoją pierwszą próbę i poruszam się po liniach podobnych do twojego drugiego akapitu. Dzięki za wskazówki w par. 3; Nie zdawałem sobie z tego sprawy. –

+0

Osobiście wolę używać kolejki (http://msdn.microsoft.com/en-us/library/7977ey2c.aspx) do komunikacji między wątkami: wątek tła dodaje elementy ("wyniki") do kolejki, Wątek interfejsu użytkownika sprawdza kolejkę w stanie bezczynności i aktualizuje formanty (jest to typowy producent/konsument). Musisz jednak uważnie rozważyć, czy tło nie może przekroczyć możliwości interfejsu użytkownika, co może łatwo nastąpić, zwłaszcza w przypadku sieci, co powoduje, że kolejka wymyka się spod kontroli, chyba że podejmiesz specjalne środki. –

1

Przykładowy kod w numerze this answer zapewnia elegancki sposób obejścia tego problemu.

Kod cytat z tej odpowiedzi:

public void UpdateTestBox(string newText) 
{ 
    BeginInvoke((MethodInvoker) delegate { 
     tb_output.Text = newText; 
    });   
} 

... chociaż w przypadku co chcesz zadzwonić BeginInvoke na samych kontroli:

public void UpdateTestBox(string newText) 
{ 
    tb_output.BeginInvoke((MethodInvoker) delegate { 
     tb_output.Text = newText; 
    });   
} 
+0

Nie do końca rozumiem, jak to pomoże. Mam na myśli, tak, masz rację, oczywiście, użycie Invoke/BeginInvoke pomoże, ale właśnie tego staram się uniknąć. A może po prostu nie zrozumiałem, do czego zmierzasz. –

+1

Jeśli utworzysz formanty w innym wątku niż wątek interfejsu użytkownika, nie zobaczę, jak uciec od Invoke/BeginInvoke. Jednak sugerowany kod jest znacznie prostszy niż wariant if (ctl.InvokeRequired) [...]. Natychmiastowa kara za wielowątkowość jest taka, że ​​musisz wykonać synchronizację;) –

1

nie znając szczegóły mechanizmu InvokeRequired , Eksperymentowałem trochę i afaik, możesz ustawić większość właściwości w wątku, o ile nie został on parentowany (tj. Dodany do niektórych właściwości Control.Controls).

Powinieneś być w stanie przygotować kontrole, zapisać je na liście i dołączyć je do głównego interfejsu w metodzie Invoked.


Edytuj: Myślę, że nie ma znaczenia, który wątek utworzył kontrolę. Dlatego powinny obowiązywać normalne reguły, tzn. Można pracować tylko z formantami z głównego wątku systemu Windows. I myślę, że kryterium jest HandleCreated zamiast Parented. Dopóki to się nie wydarzy, dostaniesz odrobinę wytchnienia.

+0

Tak, wygląda na to, że to rodzicielstwo mnie zabija. Twój pomysł nie jest zły, ale nie pomoże tutaj, ponieważ właściwości, które ustawiam, są odpowiedzią na niektóre interakcje użytkownika, innymi słowy * po * kontrola została sprowadzona do głównego formularza i wyświetlona. To mogło nie być całkiem jasne z mojego pytania. –

+0

Było jasne, ale robię to bez problemów. –

Powiązane problemy