Problém s vlákny – .NET – Fórum – Programujte.com
 x   TIP: Přetáhni ikonu na hlavní panel pro připnutí webu

Problém s vlákny – .NET – Fórum – Programujte.comProblém s vlákny – .NET – Fórum – Programujte.com

 

Toto vlákno bylo označeno za vyřešené — příspěvek s řešením.
20. 8. 2012   #1
-
0
-

Dobrý den,

vytvářím prvně aplikaci s vlákny a nějak mi to nejde.    Potřebuju udělat aplikaci, kde poběží hlavní vlákno a akorát při spuštětí aplikace a nebo po uplynutí zadaného časového intervalu se spustí druhé vlákno (pracovní), které provede určitý úkol.  Potřebuju ale ještě, aby když probíhá pracovní vlákno a uživatel zmáčkne nějaké tlačítko, aby se pracovní vlákno zastavilo, hlavní vlákno zpracovalo požadavek uživatele a po jeho splnění se opět spustilo pracovní vlákno a dodělalo svůj úkol.

Jednoduchá aplikaci, na které si chci zprovoznit tuto funkčnost, než ji dám do skutečné aplikace. Pracovní vlákno se spouští pří startu a nebo po uplynutí času v Timeru1. Button2 simuluje požadavek od uživatele, má vyvolat pozastavení pracovního vlákna. Button3 simuluje splnění požadavku od uživatele a má opětovně spustit pracovní vlákno.

public partial class Form1 : Form
    {
        private int max = 100000000;
        Thread pracovni;
        
        public Form1()
        {
            InitializeComponent();
            progressBar1.Minimum = 0;
            progressBar1.Maximum = max;
            progressBar1.Value = 0;

            Thread.CurrentThread.Name = "hlavni";
            pracovni = new Thread(Inkrement);
            pracovni.Name = "pracovni";

            timer1.Interval = 1000;
            timer1.Start();
            
            pracovni.Start();
        }

        void Inkrement()
        {
            int i = 0;
            while (i != max)
            {
                i++;
                progressBar1.Increment(1);
                // InvalidOperationException - Cross-thread operation not valid: Control 'progressBar1' accessed 
                //from a thread other than the thread it was created on.
            }
            progressBar1.Value = 0;
            //restart
        }
        
        private void button2_Click(object sender, EventArgs e)
        {
            MessageBox.Show("Zastav inkrementaci");
            pracovni.Join();
        }

        private void button3_Click(object sender, EventArgs e)
        {
            MessageBox.Show("Opet bez");
            pracovni.Start();
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            MessageBox.Show("Samorozjezd");
            pracovni.Start();
        }
    }

Problém je v metodě Inkrement. Když dojde k úpravě hodnoty Value progressBar1, vyskočí vyjímka uvedená v zakomentované části pod progressBar1.Increment(1);

Chápu, že nemůžu z pracovního vlákna volat objekt vytvořený v hlavním vláknu. Zkoušel jsem kód upravit s pomocí delegátů, ale poté program funguje tak, že proběhne Inkrement až do konce a nejde stisknout žádné tlačítko a Inkrementaci přerušit.    Ještě jsem musel odstranit Timer, protože pak mi jen vyskakovalo okno, že došlo k samospuštění a program skončil vyjimkou, když chtěl upravovat Value progressBar1.

 Kód s delegátem:

public partial class Form1 : Form
    {
        private int max = 1000000;
        Thread pracovni;
        
        public Form1()
        {
            InitializeComponent();
            progressBar1.Minimum = 0;
            progressBar1.Maximum = max;
            progressBar1.Value = 0;

            Thread.CurrentThread.Name = "hlavni";
            pracovni = new Thread(new ThreadStart(Inkrement));
            pracovni.Name = "pracovni";
            
            pracovni.Start();
        }
        protected delegate void SetIntDelegate(int cislo);
        protected void SetInt(int cislo)
        {
            progressBar1.Value=cislo;
        }
        void Inkrement()
        {
            lock (this)
            {
                int i = 0;
                while (i != max)
                {
                    i++;
                    BeginInvoke(new SetIntDelegate(SetInt), new object[] { i });
                }
            }
            progressBar1.Value = 0;
        }
        
        private void button2_Click(object sender, EventArgs e)
        {
            if (pracovni.ThreadState == ThreadState.Running)
            {
                MessageBox.Show("Zastav inkrementaci");
                pracovni.Join();
            }
        }

        private void button3_Click(object sender, EventArgs e)
        {
            if (pracovni.ThreadState == ThreadState.WaitSleepJoin)
            {
                MessageBox.Show("Opet bez");
                pracovni.Start();
            }
        }
    }

Byl bych moc vděčný za radu, jak to napsat, aby to fungovalo podle mých potřeb.  Děkuji moc

A ještě bych se chtěl zeptat, jestli je dobrý způsob pozastavení pracovního vlákna metodou pracovni.Join();?

Děkuji moc

Nahlásit jako SPAM
IP: 195.250.140.–
ingiraxo+15
Grafoman
20. 8. 2012   #2
-
0
-

Pro tento případ jakej si uvedl, bych doporučil BackgroundWorker, protože ten odevzdává parametr přes asyc delegáta (invokací) a nevzniká tak CrossThread error

Pokud chceš přes fyzický vlákno, tak budeš muset ručně ošetřit přesun hodnoty do ProgressBaru, protože ten se nachází v hlavním vlákně (bud anonymně pres MethodInvoker, ale ten podle mě není úplně bezpečnej, nebo klasicky přes pomocnej delegát volat Invokera - nasimulovat stejnou techniku jako u BackgroundWorkeru).

Tady máš přesně to co řešís (měnit hodnotu v progressBaru mimo hlavní vlákno) za pomocí BackgroundWorkeru - https://www.youtube.com/watch?v=f8BeDXGewr0

a co se týká toho Join(..), tak vlákno by se mělo blokovat hned po ukončení svého běhu, žádný vlákno nepozastavíš... k tomu se používá Auto-Manual resetevent, 

Nahlásit jako SPAM
IP: 213.168.183.–
Moje aplikace: http://ophite.cz
Tutoriály na: C#
reciproke0
Návštěvník
20. 8. 2012   #3
-
0
-

#1 LordFaltus

vyjímka nabízí takovéto řešení.... 

 protected void SetInt(int cislo)
        {

            if (this.progressBar1.InvokeRequired)
            {
                SetIntDelegate d = new SetIntDelegate(SetInt);
                this.Invoke(d, cislo);
            }
            else
            {
                this.progressBar1.Value = cislo;
            }

        }
        void Inkrement()
        {
                int i = 0;
                while (i != max)
                {
                    i++;
                    SetInt(i);
                }
                i = 0;
                SetInt(i);
        }

        private void button2_Click(object sender, EventArgs e)
        {
            if (pracovni.ThreadState == ThreadState.Background || pracovni.ThreadState == ThreadState.WaitSleepJoin)
            {
                MessageBox.Show("Zastav inkrementaci");
                pracovni.Suspend();
            }
        }

        private void button3_Click(object sender, EventArgs e)
        {
            if (pracovni.ThreadState == ThreadState.Suspended)
            {
                MessageBox.Show("Opet bez");
                pracovni.Resume();
            }
        }

Jinak vlákno může být ve více stavech ačkoli na venek "běží". A v návrhu použité příkazy suspend a resume jsou zastaralé kompilátor by se měl ozvat.

Lepší řešení je to na které odkazuje ingiraxo

Nahlásit jako SPAM
IP: 82.208.4.–
20. 8. 2012   #4
-
0
-

Díky ingiraxo za odkaz na video s tutoriálem. Upravil jsem podle něj kód, ale není to úplně to, co bych potřeboval.

Kód teď vypadá takto:

 public partial class Form1 : Form
    {
        private int max = 1000000;
        private int lastStav = 0;
        public Form1()
        {
            InitializeComponent();
            progressBar1.Minimum = 0;
            progressBar1.Maximum = max;
            progressBar1.Value = 0;
        }

        private void button1_Click(object sender, EventArgs e)
        {
            MessageBox.Show("Rozjed to");
            Worker.RunWorkerAsync();
        }
       
        private void button2_Click(object sender, EventArgs e)
        {
            Watcher.RunWorkerAsync();
            MessageBox.Show("Zastav inkrementaci");
         }

        private void button3_Click(object sender, EventArgs e)
        {
            MessageBox.Show("Opet bez");
            if (!Worker.IsBusy)
            {
                Worker.RunWorkerAsync();
            }
        }

        private void Worker_DoWork(object sender, DoWorkEventArgs e)
        {

            for (int i = 0; i <= max; i++ )
            {
                if (lastStav != 0)
                {
                    i = lastStav;
                }
                if (Worker.CancellationPending)
                {
                    lastStav = i;
                    break;
                }
                Worker.ReportProgress(i);
            }
        }

        private void Worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            progressBar1.Value = e.ProgressPercentage;
        }

        private void Worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            MessageBox.Show("Hotovo");
        }
        
        private void Watcher_DoWork(object sender, DoWorkEventArgs e)
        {
            Worker.CancelAsync();
        }

        
    }

BackGroundWorker skvěle posouvá progressBarem, ale tlačítka jsou jakoby zablokovaný a nejde žádné zmáčknout při běhu Workeru, takže nemůžu průběh nikdy pozastavit. Tlačítka jsou klikatelný až po doběhnutí cyklu. 

Nahlásit jako SPAM
IP: 195.250.140.–
ingiraxo+15
Grafoman
20. 8. 2012   #5
-
0
-

takže jestli tvůj problém dobře chápu, tak ty chceš přes tlačítko spustit progressbar, možnost ho kdykoliv pozastavit a opet rozjet, případně resetovat?

Nahlásit jako SPAM
IP: 213.168.183.–
Moje aplikace: http://ophite.cz
Tutoriály na: C#
Řešení
ingiraxo+15
Grafoman
20. 8. 2012   #6
-
0
-
Vyřešeno Nejlepší odpověď

Fajn, udělal jsem teda jak to má bejt, v tomhle případě stačí jeden worker, na tom videu byl pouze pro simulaci prodlevy, ale tady máš tlačítko pro pauzu, který odešle práve ukončení workera

Jsou tam 3 tlačítka.. jedno pro start/stop, druhý pro pause/resume a třetí pro reset... reset můžeš použít pouze tehdy, když je worker v režimu pauzy, ale není dokončenej (na 100%)

using System;
using System.ComponentModel;
using System.Threading;
using System.Windows.Forms;

namespace FormTest
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            bar.Maximum = 1000;
            btnPauseResume.Enabled = false;
            btnReset.Enabled = false;
        }

        private void ClickStartStop(object sender, EventArgs e)
        {
            if (!worker.IsBusy)
            {
                bar.Value = 0;
                btnStartStop.Text = "Stop";
                btnStartStop.Enabled = false;
                btnPauseResume.Enabled = true;
                btnReset.Enabled = false;
                worker.RunWorkerAsync();
            }
        }

        private void ClickPauseResume(object sender, EventArgs e)
        {
            if (worker.IsBusy)
            {
                btnReset.Enabled = true;
                btnPauseResume.Text = "Pokračovat";
                worker.CancelAsync();
            }
            else
            {
                btnReset.Enabled = false;
                btnPauseResume.Text = "Pozastavit";
                worker.RunWorkerAsync();
            }
        }

        private void Update(object sender, DoWorkEventArgs e)
        {
            for (int i = bar.Value; i <= bar.Maximum; i++)
            {
                if (worker.CancellationPending)
                    break;

                worker.ReportProgress(i);
                Thread.Sleep(1);
            }
        }

        private void UpdateChanged(object sender, ProgressChangedEventArgs e)
        {
            bar.Value = e.ProgressPercentage;
        }

        private void UpdateCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (bar.Value == bar.Maximum)
            {
                bar.Value = 0;
                btnStartStop.Text = "Start";
                btnStartStop.Enabled = true;
                btnPauseResume.Enabled = false;
            }
        }

        private void ClickReset(object sender, EventArgs e)
        {
            if (!worker.IsBusy)
            {
                bar.Value = 0;
                btnPauseResume.Text = "Pozastavit";
                btnPauseResume.Enabled = false;
                btnStartStop.Text = "Start";
                btnStartStop.Enabled = true;
                btnReset.Enabled = false;
            }
        }
    }
}
Nahlásit jako SPAM
IP: 213.168.183.–
Moje aplikace: http://ophite.cz
Tutoriály na: C#
20. 8. 2012   #7
-
0
-

#6 ingiraxo
Děkuji ti mnohokrát za pomoc ingiraxo, toto je přesně to, co jsem potřeboval.    Už to běhá podle mých představ. Ještě jednou díky. 

Nahlásit jako SPAM
IP: 89.176.228.–
Zjistit počet nových příspěvků

Přidej příspěvek

Toto téma je starší jak čtvrt roku – přidej svůj příspěvek jen tehdy, máš-li k tématu opravdu co říct!

Ano, opravdu chci reagovat → zobrazí formulář pro přidání příspěvku

×Vložení zdrojáku

×Vložení obrázku

Vložit URL obrázku Vybrat obrázek na disku
Vlož URL adresu obrázku:
Klikni a vyber obrázek z počítače:

×Vložení videa

Aktuálně jsou podporována videa ze serverů YouTube, Vimeo a Dailymotion.
×
 
Podporujeme Gravatara.
Zadej URL adresu Avatara (40 x 40 px) nebo emailovou adresu pro použití Gravatara.
Email nikam neukládáme, po získání Gravatara je zahozen.
-
Pravidla pro psaní příspěvků, používej diakritiku. ENTER pro nový odstavec, SHIFT + ENTER pro nový řádek.
Sledovat nové příspěvky (pouze pro přihlášené)
Sleduj vlákno a v případě přidání nového příspěvku o tom budeš vědět mezi prvními.
Reaguješ na příspěvek:

Uživatelé prohlížející si toto vlákno

Uživatelé on-line: 0 registrovaných, 3 hosté

Podobná vlákna

Problem s vlakny — založil Lukas

Problém s vlákny — založil GoliathL

Projekt s vlákny — založil Deyv

Peklo s vlákny — založil Sefiros

Prace s vlakny sleep/interrupt — založil rybar73

 

Hostujeme u Českého hostingu       ISSN 1801-1586       ⇡ Nahoru Webtea.cz logo © 20032024 Programujte.com
Zasadilo a pěstuje Webtea.cz, šéfredaktor Lukáš Churý