[C#]Multithreading voor downloaden files

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

  • degroot
  • Registratie: December 2003
  • Niet online
In mijn C# applicatie maak ik gebruik van multithreading.
Dit omdat als ik download anders mijn gehele applicatie bevriest.
Voor de file handling gebruik ik de System.Net class

Nu wil ik graag, d.m.v multithreading dus , zorgen dat ik 2 bestanden tegelijk kan downloaden.
Anders had een gewone thread dus wel volstaan in mijn applicatie.

Het punt is nu dat de 1e Thread start , maar de 2e Thread start pas nadat de 1e is afgelopen.
Hierdoor volgende de downloads elkaar op.
Maar wat ik wil is dus dat de downloads tegelijk starten.

Mijn vermoeden zegt dat dit probleem niet bij de Threads ligt , maar juist in de webRequest.
Hij start met de webrequest pas als de webResponse gesloten word.

Deze had ik namelijk eerst niet goed afgesloten , en dat gaf een WebException.

Als ik deze dus sluit aan het einde van de functie , dan werken de threads wel , maar volgen elkaar dus op.
Ik wil graag dat deze gelijktijdig lopen , zodat mijn downloads tegelijk binnen komen.

Het probleem is dus dat ik hier een beetje vastloop , en eigenlijk niet goed meer weet waar ik het moet zoeken.
Ik zit zelf te denken aan iedere keer dat de functie start , en compleet nieuwe webRequest aanmaken met een unieke waarde zodat ze gelijk tijdig kunnen lopen en niet op elkaar hoeven te wachten.

Klopt mijn redenatie? en zit ik in de goede richting?
Of is er misschien een andere oplossing m.b.t de filehandling?

Ter verduidelijking zal ik mijn functie waar het om gaat ook nog even posten
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
        private void downloadPodCast()
        {
            using (WebClient wcDownload = new WebClient())
            {
                try
                {
                    // Create a request to the file we are downloading
                    webRequest = (HttpWebRequest)WebRequest.Create(nodeTag);
   
                    // Retrieve the response from the server
                    webResponse = (HttpWebResponse)webRequest.GetResponse();
                    // Ask the server for the file size and store it
                    Int64 fileSize = webResponse.ContentLength;

                    // Open the URL for download
                    strResponse = wcDownload.OpenRead(nodeTag);
                    // Create a new file stream where we will be saving the data (local drive)
                    try
                    {
                        strLocal = new FileStream(downloadFolder + title + "_-_" + desc + "_-_" + date + ".mp3", FileMode.Create, FileAccess.Write, FileShare.None);
                    }
                    catch (DirectoryNotFoundException)
                    {
                        Directory.CreateDirectory(downloadFolder);
                        strLocal = new FileStream(downloadFolder + title + "_-_" + desc + "_-_" + date + ".mp3", FileMode.Create, FileAccess.Write, FileShare.None);

                    }
                    // It will store the current number of bytes we retrieved from the server
                    int bytesSize = 0;
                    // A buffer for storing and writing the data retrieved from the server
                    byte[] downBuffer = new byte[10000];

                    // Loop through the buffer until the buffer is empty
                    while ((bytesSize = strResponse.Read(downBuffer, 0, downBuffer.Length)) > 0)
                    {
                        // Write the data from the buffer to the local hard drive
                        strLocal.Write(downBuffer, 0, bytesSize);
                        // Invoke the method that updates the form's label and progress bar
                       form.Invoke(new UpdateProgessCallback(this.UpdateProgress), new object[] { strLocal.Length, fileSize });
                    }
                }
                finally
                {
                    // When the above code has ended, close the streams
                    webResponse.Close();
                    strResponse.Close();
                    strLocal.Close();
                }
            }
        }


EDIT:
Ik maak iedere keer een nieuwe instantie van mijn download class aan.
Daarmee zou toch ook de webRequest iedere keer uniek moeten worden? , of zit ik nu helemaal verkeerd te denken :+

www.degroot-it.nl


Acties:
  • 0 Henk 'm!

  • Creepy
  • Registratie: Juni 2001
  • Laatst online: 17-09 21:27

Creepy

Tactical Espionage Splatterer

Klopt mijn redenatie? en zit ik in de goede richting?
Probeer het gewoon?

Je download nu een file en wacht daarop. Als je elke file in een eigen thread download (met daarin dus een eigen request, stream etc. )dan kan je meerdere files tegelijk downloaden.

Edit: als je de downloadPostCast method aanroept: die start geen nieuwe thread en zal pas beeindigen als de volledige download klaar is.

[ Voor 20% gewijzigd door Creepy op 28-05-2007 10:32 ]

"I had a problem, I solved it with regular expressions. Now I have two problems". That's shows a lack of appreciation for regular expressions: "I know have _star_ problems" --Kevlin Henney


Acties:
  • 0 Henk 'm!

  • degroot
  • Registratie: December 2003
  • Niet online
Ik hoop dat mijn thread dan verder in orde is.

In mijn mainform heb ik een button die het volgende doet
C#:
1
2
download task1 = new download(nodeTag,title,desc,date,this);
task1.m_thread.Start();


En daarna start mijn download class een nieuwe thread
C#:
1
2
3
4
5
6
7
8
9
10
        public download(string nodeTag,string title,string desc , string date,mainForm form)
        {
            m_thread = new Thread(new ThreadStart(downloadPodCast));
            this.nodeTag = nodeTag;
            this.title = title;
            this.desc = desc;
            this.date = date;
            downloadFolder = podList.downFolder();
            this.form = form;
        }


En die m_thread start dus de functie downloadPodCast

Om alles even op een rijtje te krijgen...
Mijn Thread verhaal is dan toch in orde?

www.degroot-it.nl


Acties:
  • 0 Henk 'm!

  • Woy
  • Registratie: April 2000
  • Niet online

Woy

Moderator Devschuur®
Op zich ziet dat er goed uit. Al zou ik je Thread niet public maken. Als het echt iets is wat in een eigen Thread runt zou ik je class eerder zelf een start methode geven. Dan is je class zelf helemaal verantwoordelijk voor het beheer van zijn Thread.

Om het mischien nog wat verwarrender te maken. Je zou ook gebruik kunnen maken van Asynchrone communicatie. Je hoeft dan niet perse zelf nieuwe threads aan te maken. Er worden dan Threads uit de Thread pool gebruikt en als bijvoorbeeld een Read klaar is krijg je een Event binnen. ( Kijk eens naar de Begin... en End... methoden ). Ik bedoel niet dat je meteen alles maar om moet gaan gooien, maar het is wel handig om te weten welke opties je allemaal hebt.

“Build a man a fire, and he'll be warm for a day. Set a man on fire, and he'll be warm for the rest of his life.”


Acties:
  • 0 Henk 'm!

  • Infinitive
  • Registratie: Maart 2001
  • Laatst online: 25-09-2023
Nog een paar mogelijkheden:

- De server waar je vandaan download staat geen simultane downloads toe en geeft pas een antwoord op je tweede download als de eerste klaar is.
- De webclient zelf is mutual exclusive (lijkt me sterk)

Je kan het e.a. uittesten door een breakpoint op de eerste regel van je try en de laatste regel van je finally te plaatsen. Als je je threads goed maakt en je download lang genoeg duurt, dan zul je de eerste breakpoint twee keer tegenkomen, voordat je de laatste breakpoint voor het eerst tegenkomt.

putStr $ map (x -> chr $ round $ 21/2 * x^3 - 92 * x^2 + 503/2 * x - 105) [1..4]


Acties:
  • 0 Henk 'm!

  • degroot
  • Registratie: December 2003
  • Niet online
Infinitive schreef op maandag 28 mei 2007 @ 11:43:
Nog een paar mogelijkheden:

- De server waar je vandaan download staat geen simultane downloads toe en geeft pas een antwoord op je tweede download als de eerste klaar is.
- De webclient zelf is mutual exclusive (lijkt me sterk)
De server is van Radio 538.
Lijkt me sterk dus dat die geen simultane downloads toe laat
rwb schreef op maandag 28 mei 2007 @ 11:16:
Op zich ziet dat er goed uit. Al zou ik je Thread niet public maken. Als het echt iets is wat in een eigen Thread runt zou ik je class eerder zelf een start methode geven. Dan is je class zelf helemaal verantwoordelijk voor het beheer van zijn Thread.
Hoe bedoel je dit?

[ Voor 30% gewijzigd door degroot op 28-05-2007 11:53 ]

www.degroot-it.nl


Acties:
  • 0 Henk 'm!

  • whoami
  • Registratie: December 2000
  • Laatst online: 01:56
Gewoon, geef je 'Download' class een method 'Start', en die method start de thread.
Een 'task' pattern ongeveer, zoals dit:
whoami in "\[C#] button.Show() vanuit anderen Thread"

https://fgheysels.github.io/


Acties:
  • 0 Henk 'm!

  • degroot
  • Registratie: December 2003
  • Niet online
dan word de code dus zo?
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
        public download(string nodeTag,string title,string desc , string date,mainForm form,int countRows)
        {
            this.nodeTag = nodeTag;
            this.title = title;
            this.desc = desc;
            this.date = date;
            this.form = form;
            this.countRows = countRows;

   

            m_thread = new Thread(new ThreadStart(downloadPodCast));
            downloadFolder = podList.downFolder();
            m_thread.Start();
        }




        private void downloadPodCast()
        {
            using (WebClient wcDownload = new WebClient())
            {
                try
                {
                    // Create a request to the file we are downloading
                    webRequest = (HttpWebRequest)WebRequest.Create(nodeTag);
   
                    // Retrieve the response from the server
                    webResponse = (HttpWebResponse)webRequest.GetResponse();
                    // Ask the server for the file size and store it
                    Int64 fileSize = webResponse.ContentLength;

                    // Open the URL for download
                    strResponse = wcDownload.OpenRead(nodeTag);
                    // Create a new file stream where we will be saving the data (local drive)
                    try
                    {   
                        strLocal = new FileStream(downloadFolder + title + "_-_" + desc + "_-_" + date + ".mp3", FileMode.Create, FileAccess.Write, FileShare.None);
                    }
                    catch (DirectoryNotFoundException)
                    {
                        Directory.CreateDirectory(downloadFolder);
                        strLocal = new FileStream(downloadFolder + title + "_-_" + desc + "_-_" + date + ".mp3", FileMode.Create, FileAccess.Write, FileShare.None);

                    }
                    // It will store the current number of bytes we retrieved from the server
                    int bytesSize = 0;
                    // A buffer for storing and writing the data retrieved from the server
                    byte[] downBuffer = new byte[10000];

                    // Loop through the buffer until the buffer is empty
                    while ((bytesSize = strResponse.Read(downBuffer, 0, downBuffer.Length)) > 0)
                    {
                        // Write the data from the buffer to the local hard drive
                        strLocal.Write(downBuffer, 0, bytesSize);
                        // Invoke the method that updates the form's label and progress bar
                       form.Invoke(new UpdateProgessCallback(this.UpdateProgress), new object[] { strLocal.Length, fileSize });
                    }
                }
                finally
                {
                    // When the above code has ended, close the streams
                    webResponse.Close();
                    strResponse.Close();
                    strLocal.Close();
                }
            }
        }


En dan iedere keer een nieuwe klass aanroepen?


EDIT
Nu is het dus alleen nog zaak om de thread zo te maken dat het een identieke thread word toch?
Zodat ik meerdere downloads tegelijk kan uitvoeren , als ik het verhaal een beetje snap?

[ Voor 6% gewijzigd door degroot op 28-05-2007 13:28 ]

www.degroot-it.nl


Acties:
  • 0 Henk 'm!

  • whoami
  • Registratie: December 2000
  • Laatst online: 01:56
Is het zo'n probleem om iedere keer een nieuw object te instantieren dan ?

Trouwens, ik zou die thread niet starten in de constructor, maar expliciet een publieke 'Start' method maken, die dan uiteindelijk de 'downloadpodcast' method uitvoert.

https://fgheysels.github.io/


Acties:
  • 0 Henk 'm!

  • degroot
  • Registratie: December 2003
  • Niet online
whoami schreef op maandag 28 mei 2007 @ 20:17:
Is het zo'n probleem om iedere keer een nieuw object te instantieren dan ?
Een beetje wel ja , want zo'n held in programmeren ben ik nou ook weer niet.
Maar ben niet te beroerd om het uit te zoeken , zolang iemand mij even het opstapje kan even
whoami schreef op maandag 28 mei 2007 @ 20:17:
Trouwens, ik zou die thread niet starten in de constructor, maar expliciet een publieke 'Start' method maken, die dan uiteindelijk de 'downloadpodcast' method uitvoert.
Dat heb ik gedaan.
Is inderdaad veel simpeler , dan kan ik namelijk ook mijn thread vanuit mijn form weer een functie schrijven die de thread pauzeert bijvoorbeeld

www.degroot-it.nl


Acties:
  • 0 Henk 'm!

  • Woy
  • Registratie: April 2000
  • Niet online

Woy

Moderator Devschuur®
degroot schreef op maandag 28 mei 2007 @ 21:14:
[...]

Een beetje wel ja , want zo'n held in programmeren ben ik nou ook weer niet.
Maar ben niet te beroerd om het uit te zoeken , zolang iemand mij even het opstapje kan even
Een nieuw object instantieren is toch niet zo moeijlijk?
C#:
1
object o = new object();

“Build a man a fire, and he'll be warm for a day. Set a man on fire, and he'll be warm for the rest of his life.”


Acties:
  • 0 Henk 'm!

  • Fiander
  • Registratie: Februari 2001
  • Laatst online: 28-05 12:35
Kijk ook hier eens naar:
Asynchronous Requests

door asynchronous requests te combineren met de threadpool, hoef je zelf helemaal niet met threads te werken. ( hou er wel rekening mee dat er max 64 objecten gequeed kunnen staan in de threadpool )

Deze sig is een manueel virus!! Als je dit leest heb je het. Mail dit bericht naar iedereen die je kent, en verwijder alle bestanden van je computer.


Acties:
  • 0 Henk 'm!

Verwijderd

Fiander schreef op dinsdag 29 mei 2007 @ 09:44:
hou er wel rekening mee dat er max 64 objecten gequeed kunnen staan in de threadpool
Dat klinkt als een of andere standaard waarde, het lijkt me toch wel dat dit aangepast kan worden???

Acties:
  • 0 Henk 'm!

  • Fiander
  • Registratie: Februari 2001
  • Laatst online: 28-05 12:35
ik zeg het nog een beetje beroerd. het probleem met die 64 zit niet in de threadpool, maar in de waithandle met welke je het in de pool zet.

je kunt max 64 items in een waithandle zetten. en dit is een constante.
je kunt wel meerdere waithandles in een pool gooien.

hiero de implementatie zoals in rotor.
WaitHandle code

hiero wat ik uit reflector haal.
code:
1
private const int MAX_WAITHANDLES = 0x40;

[ Voor 17% gewijzigd door Fiander op 29-05-2007 10:45 ]

Deze sig is een manueel virus!! Als je dit leest heb je het. Mail dit bericht naar iedereen die je kent, en verwijder alle bestanden van je computer.


Acties:
  • 0 Henk 'm!

  • degroot
  • Registratie: December 2003
  • Niet online
rwb schreef op dinsdag 29 mei 2007 @ 09:20:
[...]

Een nieuw object instantieren is toch niet zo moeijlijk?
C#:
1
object o = new object();
Dat snap ik ook wel natuurlijk , en dat doe ik ook nu als volgt.(Even om alles op een rijtje te krijgen)

In mij main klasse maak ik een referentie naar mijn download klasse
C#:
1
download task1 = new download();


Vervolgens doe ik halverwege de main klasse een functie en daarin roep ik de functie aan die in de download klasse een nieuwe thread aanmaakt
C#:
1
task1.startThread(nodeTag, title, desc, date, this, countRows,showName);


En die functie die word aangeroepen ziet er zo uit
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public void startThread(string nodeTag, string title, string desc, string date, mainForm form, int countRows,string showName)
        {
            this.nodeTag = nodeTag;
            this.title = title;
            this.desc = desc;
            this.date = date;
            this.form = form;
            this.countRows = countRows;
            this.showName = showName;

            m_thread = new Thread(new ThreadStart(downloadPodCast));
            downloadFolder = podList.downFolder();
            m_thread.Start();
        }

Hier word dus de thread toch als een nieuw object aangemaakt?
Zoals jullie bedoelen in jullie reacties toch?
Of is dat niet wat jullie bedoelen met een nieuw object aanmaken?

Dat is dus mijn vraag

P.S ik heb het aanmaken van de nieuwe thread dus in een start methode gezet zoals whoami dat al zei

www.degroot-it.nl


Acties:
  • 0 Henk 'm!

  • whoami
  • Registratie: December 2000
  • Laatst online: 01:56
Ik zou het zo doen :P
code:
1
2
3
4
Download file1 = new Download (nodetag, title, desc, date, ... );
file1.StartDownload();
Download file2 = new Download (anothernodetag, anothertitle, ...);
file2.StartDownload();

https://fgheysels.github.io/


Acties:
  • 0 Henk 'm!

  • degroot
  • Registratie: December 2003
  • Niet online
Ok dat verhaal snap ik tot zover.

Maar het probleem is dus dat ik niet weet hoevaak ik de class download moet aanroepen.
Dat ligt maar weer net aan hoevaak de user een podcast download.

Daarom moet die classe instantie dus ook een variable naam krijgen.
Is zoiets dus mogelijk?
Dat hij dus automatisch down1 down2 down3 van de download klasse aanmaakt?

Dan lijkt mij heel het probleem in 1x verholpen toch?
Want zoals ik het nu begrijp , maar ik steeds dezelfde instantie van de klasse(down) aan , en dat moet steeds een niewe worde(down1 , down2)?!

[ Voor 14% gewijzigd door degroot op 29-05-2007 20:41 ]

www.degroot-it.nl


Acties:
  • 0 Henk 'm!

  • whoami
  • Registratie: December 2000
  • Laatst online: 01:56
Als je echt alle geinstantieerde objecten wilt bijhouden, dan moet je eens naar Collections kijken.
De vraag is, wil je van iedere podcast die je download, het 'download' object bijhouden ? Wat ga je er nog mee doen ?

https://fgheysels.github.io/


Acties:
  • 0 Henk 'm!

  • degroot
  • Registratie: December 2003
  • Niet online
Sja opzich leek het me wel leuk om de thread te kunnen pauzeren vanaf het mainForm.
Of te annuleren

www.degroot-it.nl

Pagina: 1