CheckForIllegalCrossThreadCalls - czym grozi używanie tego?

0

Witam,

Załóżmy, że w C# przeprowadzama jakąś operację w drugim wątku - może to być powiedzmy za pomocą klasy BackgroundWorker.
I teraz, oczywiscie normalnie nie moge wywołać sobie z metody DoWork na przykład uaktualnienia pozycji paska postępu. Ale jeżeli ustawie CheckForIllegalCrossThreadCalls = false, to będę mógł to zrobić i nic złego się nie stanie.

Wiem, jak trzeba by to było zrobić "prawidłowo", ale tak się zastanawiam - skoro jest taka "furtka", to można w zasadzie zapomnieć, że operacja odbywa się w innym wątku. Jednak często słyszę, że nie powinno sie tego stosować - moje pytanie - dlaczego i czym to grozi? W moim przypadku mam tylko jeden wątek główny (GUI) i jeden BackgroundWorker.

0

Furtka jest po to, żeby ułatwić poszukiwanie błędów związanych z wątkami i działa tylko w trybie Debug.

A kontrolki Windows Forms nie są thread-safe, więc oprócz tego, że może dochodzić do typowych dla wątków deadlocków i race conditions, kontrolka może utworzyć się w innym wątku (jeżeli jej Handle nie zostało jeszcze utworzone i zrobi to pierwsze odwołanie do niej) bez message loop, co spowoduje jej niestabilność.

0

Furtka jest po to, żeby ułatwić poszukiwanie błędów związanych z wątkami i działa tylko w trybie Debug.

Hm, no ale wlasnie nie do konca, bo w trybie release tez dziala i znow nic zlego sie nie dzieje, zadnymi wyjatkami tez nie rzuca.

kontrolka może utworzyć się w innym wątku (jeżeli jej Handle nie zostało jeszcze utworzone i zrobi to pierwsze odwołanie do niej) bez message loop

W ogolnym przypadku tak, ale skoro mam odwolanie tylko do progressBar-a, to chyba jest pewne, ze jest on juz utworzony i prawidlowy w momencie, gdy wywoluje RunWorkerAsync (a przeciez dopiero wtedy tworzony jest dodatkowy wątek).

Widzialem, ze CheckForIllegalCrossThreadCalls = false bylo uzywane w kilku artykułach (i to wysoko ocenianych) na CodeProject i to do dzialania w trybie release w docelowej aplikacji (a nie tylko w celach testowych), a z drugiej strony slysze ze nie powinno sie raczej tego uzywac - dlatego sie zastanawiam.

Oczywiscie w takim przypadku uzywanie CheckForIllegalCrossThreadCalls jest glupotą, skoro BackgroundWorker ma metodę ReportProgress i event ProgressChanged, czyli robi wszystko za nas - tym nie mniej, pytalem z ciekawosci.

0

Tak ale do komponentu moze odwolac sie sama platforma .NET (np. refresh). Efektem jest pojawienie sie w miejscu komponentu czerwonego X i sypniecie wyjatkiem. Dosc trudno wywolac taki efekt (musza byc sprzyjajace warunki) ale sie zdarza (nawet natrafilem na niego w kilku aplikacjach). Chyba najlatwiej na datagridview ale to takie wrozenie.

0

No tak, o tym nie pomyslalem. Zreszta, uzycie BeginInwoke i delegata to tez zadna filozofia - i tak .NET znacznie ułatwia sprawę.

0

No właśnie, jak chcemy ustawić jakąś wartość dla kontrolki to robimy:

private delegate void CompletionHandler();

private void load1work()
{
        set_combobox1();
}
private void set_combobox1()
{
     if (!InvokeRequired)
     {
           comboBox1.Text = " jakis text ";
     }
     else
     {
           // We're in a background thread, so we call ourselves
           // whilst marshalling to the UI thread
           Invoke(new CompletionHandler(set_combobox1));
     }
}

A jeżeli chcemy jakąś wartość uzyskać to jak powinno to wyglądać?
Mam problem ze zwróceniem wartości.

0

A jeżeli chcemy jakąś wartość uzyskać

Wyjasnij moze jakas wartosc chcesz uzyskac i skad?

0

Powiedzmy, że chcę w metodzie którą wykonuje wątek zrobić tak:

private void load1work()
{
        MessageBox.Show(comboBox1.Text);
}

Tutaj trzeba pewnie zrobić funkcję która zwraca comboBox1.Text

private string get_text();

I wtedy wyglądałoby to tak:

private void load1work()
{
        MessageBox.Show(get_text());
}

Tylko jakie powinno być ciało funkcji get_text() ?

0

Powiedzmy, że chcę w metodzie którą wykonuje wątek

Ale po co? Zrób to w evencie/delegacie. Wtedy bedziesz mogl bezposrednio odwolac sie do ComboBoxa. Po prostu, zrób sobie na przyklad event, ktory bedzie "sluzyl" tylko i wylacznie do wyswietlenia twojego MessageBox-a. Za kazdym razem, kiedy wątek poboczny wysle eventa, ty go przechwycisz i wyswietlisz komunikat.
Wlasnie o to chodzi, zeby w metodzie obslugujacej watek nie odwolywac sie bezposrednio do kontrolek z watku gui.

0
worm87 napisał(a)

Tylko jakie powinno być ciało funkcji get_text() ?

Jeśli dobrze zrozumiałem problem, to pomoże Ci opis metody Invoke():

public Object Invoke(
Delegate method,
Object[] args
)

(...)

Return Value
Type: System.Object

An Object that contains the return value from the delegate being invoked, or nullNothingnullptra null reference (Nothing in Visual Basic) if the delegate has no return value.


othello napisał(a)

Ale po co? Zrób to w evencie/delegacie. Wtedy bedziesz mogl bezposrednio odwolac sie do ComboBoxa. Po prostu, zrób sobie na przyklad event, ktory bedzie "sluzyl" tylko i wylacznie do wyswietlenia twojego MessageBox-a. Za kazdym razem, kiedy wątek poboczny wysle eventa, ty go przechwycisz i wyswietlisz komunikat.
Wlasnie o to chodzi, zeby w metodzie obslugujacej watek nie odwolywac sie bezposrednio do kontrolek z watku gui.

Jeżeli dobrze rozumiem proponujesz coś takiego:

event EventHandler SetTextEvent;
void Callback(object sender, EventArgs args) {
   textBox.Text = "a"; // Dowolna operacja na formie
}

void Thread1Working() {
  // To wykonuje wątek pierwszy (twórca formy)
  SetTextEvent += new EventHandler(Callback);
}

void Thread2Working() {
  // To wykonuje drugi wątek
  SetTextEvent(this, new EventArgs());
}

Otóż to nie jest dobry pomysł. W powyższym przykładzie metoda Callback() zostanie wywołana w drugim wątku, a nie w pierwszym. Eventy to nie jest uniwersalny mechanizm do synchronizacji między wątkami.

0
Hass napisał(a)

W powyższym przykładzie metoda Callback() zostanie wywołana w drugim wątku, a nie w pierwszym.

Otóż to. To co chcemy wykonać w wątku który wywołujemy w rzeczywistości wykona się w innym wątku.

Ja kombinowałem żeby zrobić coś takiego:

private string get_combobox1()
{
     if (!InvokeRequired)
     {
           return comboBox1.Text;
     }
     else
     {
           // We're in a background thread, so we call ourselves
           // whilst marshalling to the UI thread
           Invoke(new CompletionHandler(get_combobox1));
     }
}

Ale w takim przypadku tylko dla jednego warunku jest zwracanie wartości. Takiego czegoś nie da się zrobić bo wartość musi zostać zwrócona niezależnie od spełnienia warunku.

0

Worm: zacytowałem kawałek dokumentacji nie bez powodu. W Twoim przypadku wystarczy:

private string get_combobox1()
{
     if (!InvokeRequired)
     {
           return comboBox1.Text;
     }
     else
     {
           // We're in a background thread, so we call ourselves
           // whilst marshalling to the UI thread
           return (string)Invoke(new CompletionHandler(get_combobox1));
     }
}
0

@Hass

Proponowalem uzycie Invoke/BeginInvoke, event to by mogl byc tylko mily dodatek.

0

Nie bardzo rozumiem jak event mógłby być tu pomocny... Może jakiś przykład?

0

event wymusza uzycie delegatu, a delegat zostanie wykonany w watku docelowym (tym ktory odpalil event)..

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