Obsługa kontrolki z wątku - problem

0

Witam

Mam problem z poniższym kodem, ktoś może wyjaśnić (w miarę prostymi słowami) dlaczego jest lipa ? Działa, jeśli pętla while jest zakomentowana.

public Form1()
        {
            InitializeComponent();
            for (int i = 0; i < 10; i++) UpdateTextBox("Hello");
            Thread.Sleep(1000);
            Thread t1 = new Thread(Worker);
            t1.Start();
            /*while (t1.IsAlive)
            {
                Thread.Sleep(1000);
            }*/
            
            UpdateTextBox("Goodbye");
        }
        public void Worker()
        {
            for (int i = 0; i < 10; i++)
            {
                UpdateTextBox("1");
                Thread.Sleep(1000);
            }
        }
        public delegate void UpdateTextBoxCallback(string s);
        public void UpdateTextBox(string s)
        {
            if (textBox1.InvokeRequired)
            {
                textBox1.Invoke(new UpdateTextBoxCallback(this.UpdateTextBox), s);
            }
            else
            {
                textBox1.AppendText(s + " ");
                textBox1.Refresh();
            }
        }
0

Po prostu forma Ci sie nie wlaczy przy petli while, gdyz nigdy nie wyjdzie z konstruktora. Jak pozniej ma przejsc inicjalizacje i odpowiadac na zdarzenia, skoro wisi w petli while w konstruktorze? Lepiej by bylo na koncu metody Worker zaimplementowac zamkniecie formy, czy sam ten napis googbye, wtedy by wszystko dzialalo jak trzeba :)

0

Spróbowałem w ten sposób i to samo... chyba, że czego nie zauważam

    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        private void button1_Click(object sender, EventArgs e)
        {
            odpal();
        }
        private void odpal()
        {
            for (int i = 0; i < 10; i++) UpdateTextBox("Hello");
            Thread.Sleep(1000);
            Thread t1 = new Thread(Worker);
            t1.Start();
            while (t1.IsAlive)
            {
                UpdateTextBox(".");
                Thread.Sleep(1000);

            }

            UpdateTextBox("Goodbye");
        }
        public void Worker()
        {
            for (int i = 0; i < 10; i++)
            {
                UpdateTextBox("1");
                Thread.Sleep(1000);
            }
        }
        public delegate void UpdateTextBoxCallback(string s);
        public void UpdateTextBox(string s)
        {
            if (this.textBox1.InvokeRequired)
            {
                this.textBox1.Invoke(new UpdateTextBoxCallback(this.UpdateTextBox), s);
            }
            else
            {
                textBox1.AppendText(s + " ");
                textBox1.Refresh();
            }
        }
    }
0

raczej czegos nie rozumiesz :) Nie mozesz dac petli while na watku formy (glownym), bo inaczej forma Ci sie po prostu zawiesi. Na sile mozesz tam wladowac Application.ProcessMessages() do tej petli while, acz to nie bedzie ladne rozwiazanie. Po wystartowaniu watku powiniennes pozwolic formie zyc wlasnym zyciem. Jezeli chcesz ja zablokowac to po prostu zmien property Enabled = false; formy lub poszczegolnych kontrolek, a UpdateTextBox("Goodbye"); przenies na koniec metody Worker() watku.
Nigdy nie wywoluj Thread.Sleep(); na watku Formy, bo to ja po prostu zawiesi (ani sie nie odmaluje, ani chocby zdarzen o polozeniu myszy nie przechwyci, itp... od bedzie wisiala).

0

O widzisz... to rozumiem [wstyd] chodzi o to, że w głównym wątku mają się dziać dalej rzeczy po wykonaniu wątków, a dokładniej: odpalam dwa wątki t1 i t2 z maina, jak się oba zakończą chcę żeby stało się coś innego. Jest na to jakaś elegancka metoda ?
Tak na szybko pomyślałem o trzecim wątku, który sprawdzałby czy tamte oba jeszcze żyją... zaraz przetestuję.
Działa, czy to dobra metoda ? to co mi się nie podoba, to wątki deklaruję na samym początku, żebym mógł sprawdzać w wątku kontrolnym czy tamte żyją, wolałbym je jakoś do niego przekazać, tylko nie wiem jak :-/

0

Może prościej byłoby użyć nie bezpośrednio wątków, ale obiektu klasy BackgroundWorker, w łatwy sposób dzięki event-om twoja główna Forma może "dowiedzieć się" o bieżącym stanie i zakończeniu zadania w nim realizowanego. Metoda którą przypiszesz do DoWork bedzie odpalona w osobnym wątku więc nie zablokujesz sobie GUI.

Trzeci wątek też jest dobrym rozwiązaniem :) ale więcej klepania kodu ;)

0

teres... ale po co petla, ktora sprawdza czy sie watek zakonczyl? Niech na koncu metody nowego watku bedzie metoda, ktora przez invoke'a odpalisz na watku glownym formy i niech zrobi co ma zrobic po zakonczeniu dodatkowego watku :) Po co sprawdzac czy cos sie zakonczylo i odpalac jakas akcje, skoro to cos moze samo na koniec ta akcje odpalic?

0

Moze cos takiego

        public Form1()
        {
            InitializeComponent();
        }
        private void button1_Click(object sender, EventArgs e)
        {
            odpal();
        }
        private void odpal()
        {
            for (int i = 0; i < 10; i++) UpdateTextBox("Hello");
            Thread.Sleep(1000);
            backgroundWorker1.RunWorkerAsync();
        }


        public void Worker()
        {
            for (int i = 0; i < 10; i++)
            {
                UpdateTextBox("1");
                Thread.Sleep(1000);
            }
        }
        public delegate void UpdateTextBoxCallback(string s);
        public void UpdateTextBox(string s)
        {
            if (this.textBox1.InvokeRequired)
            {
                this.textBox1.Invoke(new UpdateTextBoxCallback(this.UpdateTextBox), s);
            }
            else
            {
                textBox1.AppendText(s + " ");
                textBox1.Refresh();
            }
        }

        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            Worker();
        }

        private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            UpdateTextBox("Goodbye");
        }
0
wasiu napisał(a)

teres... ale po co petla, ktora sprawdza czy sie watek zakonczyl? Niech na koncu metody nowego watku bedzie metoda, ktora przez invoke'a odpalisz na watku glownym formy i niech zrobi co ma zrobic po zakonczeniu dodatkowego watku :) Po co sprawdzac czy cos sie zakonczylo i odpalac jakas akcje, skoro to cos moze samo na koniec ta akcje odpalic?

Bo muszą się dwa wątki zakończyć, oba wykonują obliczenia. Jak się skończą, dopiero wtedy można przejść dalej i wywołać kolejną metodę (z tym, że póki co jej obsugę wrzuciłem do trzeciego wątku).

0

Nie wiem czy elegancko ale moze byc tak

//zmienne
bool f1=false;
bool f2=false;

//Odpalasz
Thread t1 = new Thread(Worker1);
t1.Start();
Thread t2 = new Thread(Worker2);
t2.Start();

//fkc
public void Worker1()
{
    .... do smth
     f1=true;
     check();
}
public void Worker2()
{
    ...do smth
    f2=true;
    check();
}
public void check()
{
    if(f1&&f2)
    {
          //odpalasz co miales odpalic jak sie skoncza 2 watki
    }
}

Można też tak

private void button1_Click(object sender, EventArgs e)
{
    Thread t = new Thread(odpal);
    t.Start();
}
private void odpal()
{
    for (int i = 0; i < 10; i++) UpdateTextBox("Hello");
    Thread.Sleep(1000);

    ManualResetEvent[] events = new ManualResetEvent[2];
    events[0] = new ManualResetEvent(false);
    events[1] = new ManualResetEvent(false);

    ThreadPool.QueueUserWorkItem(new WaitCallback(Worker1), events[0]);
    ThreadPool.QueueUserWorkItem(new WaitCallback(Worker2), events[1]);

    WaitHandle.WaitAll(events);
    UpdateTextBox("GoodBye");
}
public void Worker1(object o)
{
    ManualResetEvent ev = (ManualResetEvent)o;
    for (int i = 0; i < 10; i++)
    {
        UpdateTextBox("1");
        Thread.Sleep(1000);
    }
    ev.Set();
}
public void Worker2(object o)
{
    ManualResetEvent ev = (ManualResetEvent)o;
    for (int i = 0; i < 10; i++)
    {
        UpdateTextBox("2");
        Thread.Sleep(1000);
    }
    ev.Set();
}
0
miross napisał(a)

Można też tak

Dzięki [browar]

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