2010-01-18 15 views
5

Czy BackgroundWorker w C# Temat jest bezpieczny?Jak upewnić się, że interfejs użytkownika reaguje na używanie BackgroundWorker

Pytam bo to dostać

sterujących utworzonych na jednym wątku nie mogą być wychowywane do kontroli na innym wątku

wyjątek z nim. To jest mój kod DoWork wydarzenie:

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) 
{ 



    var openFile = document.Open(MyFileName); 
    e.Result = openFile; 
} 

gdzie document jest kontrola UI, który jest zainicjowany, gdy forma dominująca jest tworzony. Podczas Open zostaną wypełnione różne właściwości w document.

Próbowałem zmienić kod, aby wywołać, ale ten sam problem występuje nadal. tj.

document.GetType().GetMethod("Open)".Invoke(document, new object[]{MyFileName}) 

spowoduje ten sam błąd, co powyżej.

Każdy pomysł, jak manipulować kontrolką document? Innymi słowy, jak sprawić, aby powyższy kod zadziałał?

Edycja: Zasugerowano, że używam Control.Invoke, ale nadal nie działa (oba wątki powieszono). Jest to kod próbowałem:

private delegate bool OpenFile(string filePath); 
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) 
{ 



    OpenFile oF = new OpenFile(document.Open); 
    var openFile = Invoke(oF, MyFileName); // it doesn't really matter whether I use BeginInvoke or Invoke, or other Control.Invoke, the end result is the same. Both the main thread hosting the document and the thread that launches the UI hanged. 

    e.Result = openFile; 
} 

Odpowiedz

1

Chociaż to dla mnie jasne, co dokładnie rozumiemy przez nitkę bezpieczeństwa w BackgroundWorker, problemem nie jest to, że obiekt; Formanty Windows Forms są zaprojektowane do manipulowania na pojedynczym wątku (wątku UI). Nie należy manipulować obiektami Windows Forms na różnych wątkach. Można wywołać działania w wątku UI z innych wątków przy użyciu metody Control.Invoke (metoda Invoke używasz jest przez odbicie i jest całkowicie niezwiązane z tym problemu):

Invoke(new Action(MethodToRunInUIThread)); 

void MethodToRunInUIThread() { 
    // do stuff here. 
} 

Nawiasem mówiąc, to nie robi nie ma sensu używać pracownika działającego w tle, jeśli wszystko, co robisz, to manipulowanie obiektami interfejsu użytkownika.

+0

@Mehdrad, potrzebuję pracującego w tle, aby utrzymać responsywność interfejsu, co * jest * dlaczego ma sens. – Graviton

+0

@Ngu Soon Hui: Oczywiście, jeśli masz długotrwałe zadanie, które nie jest tylko operacjami interfejsu użytkownika, użyjesz tego. Jeśli * all * robisz w metodzie 'DoWork' jest manipulowanie obiektami UI, nie powinieneś używać' BackgroundWorker' i powodem jest to, że ostatecznie będziesz musiał wykonać wszystkie operacje interfejsu użytkownika w wątku UI. –

+0

Może mieć sens i może być bardziej subtelny. Wątek może po prostu powiadomić "wątek UI", że "coś nowego się wydarzyło", a następnie jest to zależne od tego, który program obsługi powiadomień musi zaktualizować interfejs użytkownika w razie potrzeby. –

0

BackgroundWorker to struktura oparta na wątkach. Wątek-bezpieczeństwo dotyczy funkcji podczas wykonywania równoczesnych zadań. Może to, o co prosisz, dotyczy kontrolek WinForm, do których dostęp uzyskuje się przez unikalny wątek wątku interfejsu użytkownika.

2

Możesz wywołać, jak zasugerował Mehrdad Afshari, lub możesz użyć zdarzenia postępu bgw, które powraca w wątku UI. Lub ukończone dzieło, które powraca również w wątku UI. Różnica między nimi polega na tym, że WorkCompleted wystrzeliwuje tylko jeden raz na końcu. Progres jest wyrzucany przez ciebie z DoWork.

+0

W jaki sposób "wykorzystać zdarzenie postępu bgw, które powraca w wątku UI" rozwiązać mój problem, a mianowicie być w stanie mieć responsywny interfejs użytkownika? – Graviton

+2

Być może jest to niezrozumienie tego, co BGW robi dla ciebie. Jeśli robisz dużo manipulacji kontrolkami interfejsu użytkownika, a to jest powolne i powoduje, że twój interfejs użytkownika powoduje brzęczyk lub zamiera, bgw nie może ci pomóc! Jednakże, jeśli robisz coś innego (poza dotykaniem elementów sterujących Ui), na przykład przechodząc do wolnego serwisu lub DB, to wykonanie tego na bgw pozwoli twojemu interfejsowi uaktywnić się.Na przykład można wykonać intensywną operację x, a następnie użyć zdarzenia postępu, aby zgłosić z powrotem do interfejsu użytkownika, aby potwierdzić operację x zakończoną, oto wyniki tymczasowe, wykonywanie operacji y następnie –

5

Nie jest to wątek, na którym polega problem polegający na tym, że próbuje wywołać metodę w kontrolce interfejsu użytkownika. Zarówno w formantach WPF, jak i WinForms można wywoływać tylko w wątku interfejsu użytkownika (z którego zazwyczaj jest jeden). Nie mówisz, którego używasz, ale musisz zadzwonić na metodę Control.Invoke dla WinForms lub Dispatcher.Invoke dla WPF.

Przedstawiona metoda odbicia Invoke() spowoduje wywołanie metody w bieżącym wątku.

+0

Próbowałem tego fragmentu kodu w moim zdarzeniu DoWork, oraz to nadal nie działa: 'this.Invoke (document.Open)' – Graviton

+0

Nadal wywołujesz niewłaściwą metodę. Jeśli "dokument" jest formantem Windows Forms, wówczas wywołasz polecenie 'document.Invoke (MyMethod)', gdzie 'MyMethod' jest delegatem, który wywołuje polecenie' document.Open'. W zależności od podpisu możesz bezpośrednio wywołać 'document.Invoke (document.Open)'. – GraemeF

+0

@ GemaemF, co się stanie, jeśli 'document.Invoke' nie będzie dostępny? – Graviton

1

Jeśli taka funkcjonalność Kontroli interfejsu użytkownika trwa tak długo, aby wykonać, może nie być wiele do zrobienia. "Zamrożenie" pojawia się, gdy w wątku UI dochodzi do długotrwałej operacji, a jeśli ta funkcja kontrolna nie została specjalnie wykonana jako bezpieczna dla wątków, musi być uruchomiona na głównym wątku.

Zwykle chcesz oddzielić funkcjonalność dokumentu od kontrolki, która ją wyświetla. W ten sposób Twój dokument może zostać załadowany na oddzielny, niezależny wątek i wyświetlony później, gdy będzie gotowy. W przeciwnym razie sama kontrola musiałaby wdrożyć wielowątkową procedurę ładowania, aby spowolnić ładowanie.

Ponieważ w komentarzach podałeś, że jest to kontrola stron trzecich, możesz mieć pecha.

0

@Graviton, powiązane zadanie z odpowiedzią znajduje się here. Osoba korzystała z usługi BackgroundWorker w celu zaktualizowania pola tekstowego, ta sama koncepcja obowiązuje (Twoja jest tylko jednym wątkiem roboczym).

+0

Tak, zgadza się, zobacz moje zaktualizowane pytanie. Zauważyłem, że oba wątki zostały zawieszone po zastosowaniu techniki określonej w linku. – Graviton

+0

Wygląda na to, że nie wprowadzono sprawdzenia InvokeRequired i nie wykonano wywołania BeginInvoke(), jak pokazano w przykładzie. InvokeRequired zapewnia, że ​​jesteś we właściwym wątku, a wywołanie za pomocą BeginInvoke() wraca do obecnej metody. Rozumiem, że ten dokument nie oferuje metod Invoke, więc użyj obiektu formularza (który może być tym, co robisz, gdy wywołujesz Invoke() w drugim przykładzie kodu). Pamiętaj tylko, że wywołanie BeginInvoke() jest jedną rzeczą ... metoda musi nadal wywoływać BeginInvoke(), dopóki InvokeRequired nie będzie fałszywe. –

0

Musisz użyć Control.BeginInvoke() w DoWork. Wykonuje to delegację asynchronicznie, dzięki czemu wątek wywołujący nie będzie "zawieszał się".

Control.Invoke() wykona delegata również na innym wątku, ale spowoduje, że wątek wywołujący będzie oczekiwał na zakończenie wątku.

Ogólnie w Windows Forms lepiej jest używać Control.BeginInvoke(), gdy tylko jest to możliwe, aby uniknąć zakleszczenia pomiędzy wątkami, które mogą wystąpić, gdy jeden wątek czeka na inny, tak jak w przypadku Control.Invoke().

Jeśli obiekt "document" dziedziczy po System.Windows.Forms.Control, możesz po prostu wywołać document.BeginInvoke (myDelegate).

Jeśli jednak jest to jakiś inny komponent, który zawiera elementy sterujące interfejsu GUI, może ujawnić sposób wywołania BeginInvoke. Sprawdź dokumentację (jeśli istnieje). Jeśli nie ma takiej możliwości, to prawdopodobnie nie jest po prostu zaprojektowany do obsługi aplikacji wielowątkowych.

Wygląda na to, że nie masz pewności co do różnych typów Invoke/BeginInvoke (zrozumiałe). To wcześniejsze pytanie: odpowiedź What is the difference between Invoke and BeginInvoke? i Jon Skeets powinny pomóc wyjaśnić pewne kwestie.

+0

Rozumiem, ale kiedy nazwałem 'BeginInvoke', wątek zawierający" dokument "i inny wątek, który uruchamia ruchome okno dialogowe, został zawieszony. – Graviton

Powiązane problemy