Wywoływanie zdarzenia z innego wątku

0

Witam,

Proszę szanownych kolegów o pomoc w zsynchronizowaniu wątków.

Napisałem klasę:

    class KlasaTestowa
    {
        public delegate void IncomingTest();
        public delegate void IncomingTest1();

        public event IncomingTest Tests;
        public event IncomingTest1 Tests1;

        private Thread ThreadRead;


        public KlasaTestowa()
        {
            this.ThreadRead = new Thread(Communication);
            this.ThreadRead.Start();
        }
        

        private void Communication()
        {
            if (Tests != null)
                Tests();      // tu rzucany jest wyjątek

            if (Tests1 != null)
                Tests1();
        }
}

W taki sposób z niej korzystam:

public partial class FormMain : Form
{
    KlasaTestowa tt = new KlasaTestowa();

    (...)

    public FormMain()
    {
        this.tt.Test += Call;
        this.tt.Test1 += Call2;
    }

    private void Call()
    {
        this.richTextBox1.Text += "nanananna";
    }
}

A oto treść wyjątku:

ex {"Nieprawidłowa operacja między wątkami: do formantu 'richTextBox1' uzyskiwany jest dostęp z wątku innego niż wątek, w którym został utworzony."} System.Exception {System.InvalidOperationException}

Nie bardzo wiem jak temu zaradzić, czy moglibyście mi pomóc?

Dziękuję.

Pozdrawiam,
Piotr.

0

Ten kod się nie skompiluje. Metody Call w ogóle nie powinno wywołać, chyba, że nie wkleiłeś całego kodu. A co do pytania poczytaj sobie o Control.Invoke i BeginInvoke. Pozdrawiam.

0

@litrmleka: Kod się skompiluje, a co do metody call to może ją wywołać ale nie musi bo najpierw uruchamia nowy wątek, a potem dopiero podpina zdarzenie, z tym, że przy wywołaniu metody Start() niekoniecznie wątek od razu zostanie przełączony. @HIOB Najlepiej jak poczytasz sobie o powinowadztwie do wątku, bo są różne metody przełączenia do odpowiedniego wątku. Jest uniwersalny synchronizationContext, dla winform jest tak jak napisał @litrmleka control.Invoke, dla wpf jest Dispatcher.Invoke itp.

0

Nie możesz tak tego robić. Wszystkie operacje na GUI mogą być wykonywane tylko przez wątek, który to GUI utworzył (zazwyczaj wątek główny aplikacji). To znaczy, że nie możesz się odwoływać do elementów GUI bezpośrednio z innego wątku. Jest to związane z komunikatami Windows. Do takich celów musisz użyć Invoke.

Powinno to wyglądać tak, że masz klasę, która ma dostęp do gui:

public void Write(string txt)
{
  textBox.Text = txt;
}

Teraz powinieneś to zmienić w taki sposób:

private void DoWrite(string txt)
{
  textBox.Text = txt;
}

public void Write(string txt)
{
  if(textBox.InvokeRequired)
    textBox.Invoke(() => DoWrite(txt));
  else
    DoWrite(txt);
}

Metoda DoWrite jest tu tylko potrzebna do tego, żeby nie duplikować kodu. Bo jak widzisz, w metodzie Write mamy dwa kierunki - InvokeRequired -> tu pójdzie jeśli metoda została wywołana z innego wątku niż wątek tworzący textBoxa; not InvokeRequired -> tu pójdzie, jeśli metoda została wywołana z wątku tworzącego GUI.

Zauważ, że jeśli metoda Write zostanie wywołana z innego wątku niż ten, który utworzył GUI (InvokeRequired), to operacja DoWrite niekoniecznie zostanie wykonana dokładnie w tym momencie. Jej wykonanie (a właściwie przypisanie tekstu do TextBox) może zostać odłożone w czasie. Po prostu do kolejki komunikatów wątku GUI zostanie dodany nowy komunikat (przypisz tekst do TextBox), który zostanie obsłużony w odpowiednim momencie. Użytkownik zazwyczaj nie zauważy tego. Ale dla programisty może mieć to duże znaczenie.

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