Avatar billede pfp Nybegynder
30. september 2005 - 18:03 Der er 14 kommentarer og
2 løsninger

Threads og progressBar

Hej,

Jeg ved der har været bunker af spm. som dette, men jeg prøver til stadighed at forstå hvordan det fungerer med threads. Har endnu ikke fundet en artikel som forklarer det så jeg kan forstå det :(

Jeg har herunder 2 kode eksempler, og jeg håber så der er en flink ekspert der kan hjælpe mig til at forstå hvad jeg gør forkert.

private void button1_Click(object sender, EventArgs e)
        {
            int noOfSteps = 100;

            for (int i = 0; i < noOfSteps; i++)
            {
                Thread.Sleep(100);
                progressBar1.PerformStep();
            } 
        }

Ovenstående kode opdaterer min progressBar som den skal, men resulterer i at min GUI "hænger". Derfor skal der noget trådning ind over.

Mit umiddelbare forsøg er så:
private void DoWork()
        {
            int noOfSteps = 100;

            for (int i = 0; i < noOfSteps; i++)
            {
                Thread.Sleep(100);
                progressBar1.PerformStep();
            }
        }

        private void button1_Click(object sender, EventArgs e)
        {
            t = new Thread(new ThreadStart(DoWork));
            t.Start();
        }

Det compiles fint, men fejler på runtime, fordi jeg nu foretager "cross-thread operation", og det er så her kæden hopper af for mig. Hvordan skal opdatere min progressBar, hvis jeg ikke må tildele den værdier fra min thread t?

Håber virkelig der er en der kan forklare mig hvordan det hænger sammen, eller alternativt kan pege mig retning af en god pædagoisk artikel/bog om trådning.

På forhånd tak og god weekend.

/pfp
Avatar billede kalp Novice
30. september 2005 - 18:18 #1
mon det er fordi du har sat den til en knap?

og at du skal bruge et Objekt til at klare det med? I java er der en klasse som hedder Timer.. der må være noget lignende i C#
Avatar billede kalp Novice
30. september 2005 - 18:19 #2
Avatar billede pfp Nybegynder
30. september 2005 - 18:22 #3
Hej kalp,

Jeg kender godt timer controllen, og det kan godt være jeg skal bruge den, men i så fald er jeg mest ude efter at vide hvorfor jeg skal anvende den i denne sammenhæng?

Du siger at jeg måske skal kalde tråden fra et objekt, ret mig hvis jeg tager fejl, men min button1 er vel også et objekt instantieret ud fra Button-classen?
Avatar billede driis Nybegynder
30. september 2005 - 18:35 #4
Som du allerede har fundet ud af er det ikke smart at lave længerevarende arbejde i GUI tråden, idet GUI'et så ikke kan opdatere sig selv. Du kan løse problemet med events, idet du så er sikker på at GUI kaldet udføres i GUI trådens kontekst. Eksempel:

    public class Worker
    {
        public delegate void ProgressStep();
        public event ProgressStep Progress;

        protected virtual void OnProgress()
        {
            if ( Progress != null )
                Progress();
        }

        public void Work()
        {
            for ( int i = 0 ; i < 100 ; i++ )
            {
                // Do some work
                Thread.Sleep(100);
                // fire progress event
                OnProgress();
            }
        }
    }

Og i din Form eller lign:


        private void button1_Click(object sender, System.EventArgs e)
        {
            Worker worker = new Worker();
            worker.Progress += new Worker.ProgressStep(worker_Progress);
            Thread t = new Thread(new ThreadStart(worker.Work));
            t.Start();
        }

        private void worker_Progress()
        {
            this.progressBar1.PerformStep();
        }
Avatar billede pfp Nybegynder
30. september 2005 - 18:41 #5
Hej driis,

Jeg prøver lige at kiggge på det og lege lidt med det i aften, syntes det virker meget omstændigt, men sådan skal det måske være :)

Jeg vender tilbage :)
Avatar billede driis Nybegynder
30. september 2005 - 19:15 #6
Det kan godt se omstændigt ud i sådan et kort eksempel; men i et større system betyder det ikke noget.

Du får også andre fordele, idet du kan have forskellige klasser der arver fra Worker der gør forskellige ting, som alle har muligheden for at sende Progress events ud. Endelig er opdateringen af Progressbar'en løst koblet, så hvis du ønsker at gøre noget andet når der sker fremgang, er du kun nødt til at ændre i din GUI kode.
Avatar billede pfp Nybegynder
30. september 2005 - 19:20 #7
Det lyder egentlig meget fornuftigt. Jeg måske ca. 20 metoder som kræver trådning, dem er der vel ikke noget i vejen for at placere i samme Worker class?
Avatar billede driis Nybegynder
30. september 2005 - 19:45 #8
Du kan sagtens placere flere "arbejdsmetoder" i samme klasse, det er der intet i vejen for.
Avatar billede pfp Nybegynder
30. september 2005 - 19:46 #9
driis -> Nu har jeg forsøgt at afprøve dit eksempel i praksis, og det giver stadig problemer med "cross-thread operation" når worker_Progress() kaldes..
Avatar billede driis Nybegynder
30. september 2005 - 20:00 #10
Hmm, mystisk. Det virker fint her. Kan du evt. poste præcist det kode der giver problemet ?
Avatar billede pfp Nybegynder
30. september 2005 - 20:05 #11
private void button1_Click(object sender, System.EventArgs e)
        {
            Worker worker = new Worker();
            worker.Progress += new Worker.ProgressStep(worker_Progress);
            Thread t = new Thread(new ThreadStart(worker.Work));
            t.Start();
        }

        private void worker_Progress()
        {
            this.progressBar1.PerformStep();
        }

Selve worker klassen har jeg bare copy-pastet herfra.

Når jeg kører i debug mode i Visual Studio bliver der smidt en exception (InvalidOperationException) på runtime, og editoren hopper til linien:
this.progressBar1.PerformStep();

Du får lige den præcise exception beskrivelse:
Cross-thread operation not valid: Control 'progressBar1' accessed from a thread other than the thread it was created on.
Avatar billede cjensen Nybegynder
30. september 2005 - 20:20 #12
Bare fordi du bruger delegates/events opnår du ikke, at koden afvikles på GUI-tråden.
Du skal bruge (Begin)Invoke.

Fx:
private delegate void UpdateBarValueDelegate(int value);
private void UpdateBarValue(int value)
{
  if (this.InvokeRequired)
  {
    this.Invoke(new UpdateBarValueDelegate(UpdateBarValue),
        new object[] {value});
    return;
  }
  progressBar1.Value = value;
}

og fra tråden:
private void DoWork()
        {
            int noOfSteps = 100;

            for (int i = 0; i < noOfSteps; i++)
            {
                Thread.Sleep(100);
                UpdateBarValue( value );
            }
        }
Avatar billede pfp Nybegynder
30. september 2005 - 20:54 #13
Hej cjensen,

Det gav pote. Jeg forstår ikke 100% hvad der sker, men det er da et godt udgangspunkt for at jeg kan sætte mig ned og læse lidt om Invoke og Delegates i weekenden.
Avatar billede clausc Nybegynder
03. oktober 2005 - 11:52 #14
Læs osse om MethodInvoker i SDK'et (og de tilhørende samples)
Avatar billede pfp Nybegynder
05. oktober 2005 - 08:05 #15
Tak claus.

Jeg fordeler point's imellem Jer. Håber det er ok.

/pfp
Avatar billede cjensen Nybegynder
05. oktober 2005 - 17:49 #16
Tak for point - og glad for at kunne hjælpe :)
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
IT-kurser om Microsoft 365, sikkerhed, personlig vækst, udvikling, digital markedsføring, grafisk design, SAP og forretningsanalyse.

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester