Avatar billede 123maka Nybegynder
14. maj 2009 - 01:19 Der er 11 kommentarer og
2 løsninger

Trådbaserede løsning, paralelle løkker

Hej Eksperter!

Jeg sidder her med en lidt langsom applikation. Jeg har lavet en webcrawler som indekserer en række sider for materiale. Og jeg har fundet ud af at den kan være langt mere effektiv hvis den crawler to sider ad gangen (eller flere).

Jeg har så fået refaktoreret min kode så den kan køre flere gange samtidig, uden at de peger på samme objekter. Dette betyder den følgende kode kan skrives:

Webfetch wf1 = new Webfetch("http://newz.dk");
wf1.Run();

Webfetch wf2 = new Webfetch("http://reddit.org");
wf2.Run();

Webfetch wf3 = new Webfetch("http://slashdot.org");
wf3.Run();

Disse metoder ville sagtens kunne køre parallelt istedet for efter hinanden. Alternivt kunne jeg køre tre programmer med hver deres WebFetch uden problemer.

Men nu ville jeg gerne vide hvordan jeg kunne køre de tre metoder samtidigt? Hvordan gør man dette? De skal jo bare køre til de slutter, og kan de vise progress tilbage til main-tråden?
Avatar billede arne_v Ekspert
14. maj 2009 - 01:26 #1
wf1.Run();

->

(new Thread(new ThreadStart(wf1.Run))).Start();
Avatar billede arne_v Ekspert
14. maj 2009 - 01:27 #2
De kunne tælle et eller andet op når de var færdige.
Avatar billede 123maka Nybegynder
14. maj 2009 - 02:36 #3
Tælle op hvordan? Min webcrawler arbejder lidt langsomt da jeg ikke vil DDoS'e nogles servere.

Jeg har følgende kode:
    class ThreadTest
    {
        int threadNum = 0;
        public ThreadTest(int num)
        {
            this.threadNum = num;
        }
        public void Run()
        {
            int test = 1;
            while (test <= 11)
            {
                Console.WriteLine(this.threadNum + " - " + test);
                test++;
            }
        }
    }
Og min main:
            ThreadTest tt1 = new ThreadTest(1);
            ThreadTest tt2 = new ThreadTest(2);
            ThreadTest tt3 = new ThreadTest(3);

            (new Thread(new ThreadStart(tt1.Run))).Start();
            (new Thread(new ThreadStart(tt2.Run))).Start();
            (new Thread(new ThreadStart(tt3.Run))).Start();

Output:
1 - 1
1 - 2
1 - 3
1 - 4
1 - 5
1 - 6
1 - 7
1 - 8
1 - 9
1 - 10
1 - 11
3 - 1
3 - 2
3 - 3
3 - 4
3 - 5
3 - 6
2 - 1
2 - 2
2 - 3
2 - 4
2 - 5
2 - 6
2 - 7
2 - 8
2 - 9
2 - 10
2 - 11
3 - 7
3 - 8
3 - 9
3 - 10
3 - 11

Det virker som om de enkelte tråde arbejder efter hinanden. Er der en måde så de enkelte tråde kan melde deres arbejde tilbage bedre?

Mine arbejdsopgaver kan nemlig nemt regnes til % og vil også gerne vise hvor hurtigt det sker samt en samlet tid på hvornår den er færdig. Kan det lade sig gøre?
Avatar billede arne_v Ekspert
14. maj 2009 - 03:06 #4
Det tager jo ingen tid at tælle til 11.

        public void Run()
        {
            int test = 1;
            while (test <= 11)
            {
                Console.WriteLine(this.threadNum + " - " + test);
                test++;
                // simuler at noget tager tid
                Thread.Sleep(100);
            }
        }
Avatar billede arne_v Ekspert
14. maj 2009 - 03:07 #5
De kan godt opdatere en fælles variabel, hvis:
1) de har en ref til den
2) synkroniserer adgangen til den
Avatar billede 123maka Nybegynder
14. maj 2009 - 03:22 #6
En lille threadsleep hjalp på outputtet.

Jeg kunne godt tænke mig information som viser progress således:
Tråd1 - newz.dk - 20% - 123 minutter tilbage
Tråd2 - diggcom - 21% - 123 minutter tilbage
Tråd3 - filmzdk - 10% - 123 minutter tilbage

Som ikke bare voldspammer min Console. Kræver dette ikke en consumer? Eller laver man en tråd som kan referere til alle trådene, som spørger dem om deres progress og så udskriver?
Avatar billede arne_v Ekspert
14. maj 2009 - 03:48 #7
Som jeg beskrev i #5.

Lad trådene have en ref til et fælles objekt, tæl op i det (synkroniseret !).
Avatar billede 123maka Nybegynder
14. maj 2009 - 12:55 #8
Ja, jeg er bare lidt i tvivl, trådning er lidt nyt for mig. Men jeg har prøvet at lege lidt med locking nu. Det virker dog ikke helt.

Min main ser således ud:
ThreadCounter tc = new ThreadCounter();
ThreadTest tt1 = new ThreadTest(1,tc);
ThreadTest tt2 = new ThreadTest(2,tc);
ThreadTest tt3 = new ThreadTest(3,tc);

(new Thread(new ThreadStart(tt1.Run))).Start();
(new Thread(new ThreadStart(tt2.Run))).Start();
(new Thread(new ThreadStart(tt3.Run))).Start();
(new Thread(new ThreadStart(tc.Run))).Start();

ThreadCounter:
    class ThreadCounter
    {
        Dictionary<int, String> stats = new Dictionary<int,string>();

        public void AddStats(int ThreadID, string data)
        {
            lock (this.stats)
            {
                if (stats.ContainsKey(ThreadID))
                {
                    stats[ThreadID] = data;
                }
                else
                {
                    stats.Add(ThreadID, data);
                }
            }
        }

        public void Run()
        {
            lock (this.stats)
            {
                Console.Clear();
                foreach (string item in stats.Values)
                {
                    Console.WriteLine(item);
                }
            }
        }
    }

Og threadtesten:
    class ThreadTest
    {
        int threadNum = 0;
        ThreadCounter tc;
        public ThreadTest(int num, ThreadCounter tc)
        {
            this.threadNum = num;
            this.tc = tc;
        }
        public void Run()
        {
            int test = 1;
            while (test <= 11000)
            {
                //Console.WriteLine(this.threadNum + " - " + test);
                lock (this.tc)
                {
                    this.tc.AddStats(this.threadNum, "Tråd " + this.threadNum + ": " + test);
                }
                Thread.Sleep(100);
                test++;
            }
        }
    }

Umiddelbart viser den kun følgende:
Tråd 1: 1
Tråd 3: 1
Tråd 2: 1

Og så virker det som om at ThreadTest.Rum aldrig nogensinde bliver kørt igen. Ved hjælp af debugging kan jeg se at værdierne i ThreadTestene fint tæller op. Nogen ide om hvad det skyldes?
Avatar billede sirius Nybegynder
14. maj 2009 - 19:49 #9
Prøv med følgende kode:
ThreadCounter tc = new ThreadCounter();
ThreadTest tt1 = new ThreadTest(1,tc);
ThreadTest tt2 = new ThreadTest(2,tc);
ThreadTest tt3 = new ThreadTest(3,tc);

(new Thread(new ThreadStart(tt1.Run))).Start();
(new Thread(new ThreadStart(tt2.Run))).Start();
(new Thread(new ThreadStart(tt3.Run))).Start();

ThreadCounter:
    class ThreadCounter
    {
        Dictionary<int, String> stats = new Dictionary<int,string>();

        public void AddStats(int ThreadID, string data)
        {
            lock (this.stats)
            {
                if (stats.ContainsKey(ThreadID))
                {
                    stats[ThreadID] = data;
                }
                else
                {
                    stats.Add(ThreadID, data);
                }
                Run();
            }
        }

        public void Run()
        {
            Console.Clear();
            foreach (string item in stats.Values)
            {
                Console.WriteLine(item);
            }
        }
    }

Og threadtesten:
    class ThreadTest
    {
        int threadNum = 0;
        ThreadCounter tc;
        public ThreadTest(int num, ThreadCounter tc)
        {
            this.threadNum = num;
            this.tc = tc;
        }
        public void Run()
        {
            int test = 1;
            while (test <= 11000)
            {
                //Console.WriteLine(this.threadNum + " - " + test);
                this.tc.AddStats(this.threadNum, "Tråd " + this.threadNum + ": " + test);
                Thread.Sleep(100);
                test++;
            }
        }
    }
Avatar billede 123maka Nybegynder
17. maj 2009 - 23:18 #10
Min threadcounter-tråd havde ikke en while-løkke kørende. Så det var klart at den kun viste output en gang, og da tråden stadig var aktiv var det lidt svært at debugge.

Jeg tror man må gøre det sig en vane at suspende tråde som har udført deres arbejde. Det gør det nemmere at debugge.

Men det hele virker faktisk nu :) Og tak for din kodestump sirius, selvom det ikke hjalp så meget, men gav lidt renere kode (uden lock i min ThreadTest)

Hvad siger i til følgende:
arne_v: 150
sirius: 50
Avatar billede arne_v Ekspert
18. maj 2009 - 01:15 #11
svar
Avatar billede sirius Nybegynder
18. maj 2009 - 14:48 #12
svar
Avatar billede arne_v Ekspert
04. juli 2009 - 03:39 #13
Så mangler du bare at acceptere.
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