[c#/multithreading] hoe background worker te gebruiken

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

  • shades
  • Registratie: September 2001
  • Laatst online: 14-07 13:45
Ik ben bezig met een windowsapplicatie C# 3.5 (maar werkt waarschijnlijk vanaf 2.0). De applicatie is niet meer dan een schil voor een component (ook c#) die een aantal bewerkingen met .csv bestanden moet uitvoeren.

De component heeft twee events. Ze geven beide meldingen van de huidige actie. De 1ste geeft simpele melding (ben met deze file bezig en ben die actie aan het uitvoeren) de tweede geeft verbose meldingen waarin je overdonderd wordt met wat er allemaal gebeurd (das heel handig als er een fout in een csv bestand zit.

Ik heb de hoofdapplicatie op allebei de events laten "abonneren" en de verbose meldingen gaan naar een logvenster, de normale meldingen gaan naar het status labeltje. Niet echt spannend maar nu moet het in een nieuwe thread (omdat het soms heel lang duurt). Gewoon een nieuwe opstarten levert cross-thread problemen op.

Op deze lokatie een prima voorbeeld gevonden, ook het voorbeeld van David M Morton wat er onderstaat kan ik wel redelijk doorgronden maar nu moet ik het met de component doen en die twee events er ook nog op los laten maar dat snap ik niet helemaal. Ben nooit een kei geweest in event en delegates

http://msdn.microsoft.com...del.backgroundworker.aspx

Met deze code start in de component op (via een knop) en daaronder wat er verder met de events gebeurd:
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
private void btnStartConversion_Click(object sender, EventArgs e)
{
    // start conversion
    csv2kmllib.CsvConverter converter = new csv2kmllib.CsvConverter();

    converter.VerboseConversionLogEvent += new csv2kmllib.VerboseConversionLogHandler(converter_VerboseConversionLogEvent);
    converter.ProcessConversionLogEvent += new csv2kmllib.ProcessConversionLogHandler(converter_ProcessConversionLogEvent);
    converter.Go();
}

private void converter_VerboseConversionLogEvent(object sender, string message)
{
    AddLogEntry(message); // naar een listbox
}
private void converter_ProcessConversionLogEvent(object sender, string message)
{
    lblCurrentConversionProcess.Text = message;
}


Ik snap niet helemaal hoe ik dit moet vertalen naar een backgroundworker. Heeft iemand wel eens zoiets gedaan? Ik zou graag een beetje opweg worden geholpen.

Ik heb wel zitten kijken naar de InVokeRequired en de this.InVoke. Dat ging a.d.v. een voorbeeldje wel oke maar volgens mij zou het als je het goed wilt doen allemaal via de backgroundworker moeten (en ik begreep ook niet helemaal wat ik aan het doen was 8)7 ). Moet ik dat InVoke testje bijvoegen ?

https://k1600gt.nl


Acties:
  • 0 Henk 'm!

  • Matis
  • Registratie: Januari 2007
  • Laatst online: 18:39

Matis

Rubber Rocket

als je cross thread events wilt wijzigen kun je misschien hier even kijken ;)

toaomatis in "\[C#] Events aanroepen vanuit verschillen..."

If money talks then I'm a mime
If time is money then I'm out of time


Acties:
  • 0 Henk 'm!

  • shades
  • Registratie: September 2001
  • Laatst online: 14-07 13:45
Djeez.. die is heb je vandaag geplaatst en dus gewoon overheen gekeken :z 8)7
Dat is dus met die invoke. Dat werkt wel aardig maar ik hoor (lees) overal dat je beter die backgroundworker kan gebruiken en daar snap ik niets van. In een klein testje met InVoke die ik heb gemaakt (nieuw componentje die event raised, is eigenlijk hetzelfde als wat ik in mij applicatie terugkrijg ) werkt het maar je moet per Control een eigen delegate maken.. Dat lijkt me toch niet de bedoeling..

Ik ga wel je draadje even goed bekijken wat leerzaam is het zeker wel.. Whoami komt ook altijd wel met zeer wijze woorden...

[ Voor 4% gewijzigd door shades op 05-10-2008 23:07 ]

https://k1600gt.nl


Acties:
  • 0 Henk 'm!

  • Matis
  • Registratie: Januari 2007
  • Laatst online: 18:39

Matis

Rubber Rocket

shades schreef op zondag 05 oktober 2008 @ 22:32:
Dat lijkt me toch niet de bedoeling..
En dat is het ook niet! :)

Het enige wat je hoeft de doen is die GUI.SetControlPropertyValue(); aanroepen en de volgende argumenten meegeven:

Allereerst de control, in mijn geval GUI.textBox12, of GUI.textBox10, of GUI.label133, voorwaarde is wel dat control benaderbaar is vanuit die aanroep klasse. Ik heb hiervoor desbetreffende controls protected gemaakt, omdat de aanroep klasse een andere is dan de GUI klasse. Dit hoeft natuurlijk niet het geval bij jou te zijn.
Het tweede argument is het type van de control je wilt aanpassen.

Bij een textBox is dit waarschijnlijk het textBox.Text gedeelte. Dit vul je dus in in het tweede gedeelte.

Het derde argument is de (eventuele) type wat je gewijzigd wilt hebben in de desbetreffende control ( 1e argument) op de desbetreffende property ( 2e argument).

GUI.SetControlPropertyValue(GUI.textBox14,"Text","Activated");

dit is een regel uit mijn code, deze regel zet dus in de klasse GUI in de control textBox14 de property Text op "Activated".

textBox14.Text = "Activated"; zal hetzelfde doen wanneer je vanuit dezelfde thread zou werken ;)

Hoop dat het een beetje duidelijk is.

Die backgroundworker is een stuk lastiger te implementeren en resulteerd soms in vage errors.

Ook is het niet echt backwards compattible op oudere .net versies.

Ik gebruik dit nu al een tijdje en het werkt prima.

If money talks then I'm a mime
If time is money then I'm out of time


Acties:
  • 0 Henk 'm!

  • shades
  • Registratie: September 2001
  • Laatst online: 14-07 13:45
toaomatis schreef op zondag 05 oktober 2008 @ 23:24:
[...]


En dat is het ook niet! :)

Het enige wat je hoeft de doen is die GUI.SetControlPropertyValue(); aanroepen en de volgende argumenten meegeven:

--knip--
Klinkt goed.. ga d'r eens lekker mee stoeien alvorens dit te implementeren in de applicatie. Is wel handig om te doorgronden wat er gebeurd..

edit: ow wacht even. Je gaat ervan uit dat ik die SetControlPropertyValue aanroep vanuit de component. Dat kan helaas niet (is een dll) maar volgens mij moet ik wel e.e.a. kunnen als ik het ombouw.. denk ik :|

[ Voor 79% gewijzigd door shades op 06-10-2008 08:42 ]

https://k1600gt.nl


Acties:
  • 0 Henk 'm!

  • D-Raven
  • Registratie: November 2001
  • Laatst online: 10-09 20:32
Het is zo moeilijk niet om een backgroundworker te gebruiken.

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
backgroundWorker.DoWork += new DoWorkEventHandler(this.backgroundWorker_DoWork);
backgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(this.backgroundWorker_RunWorkerCompleted);
backgroundWorker.ProgressChanged += new ProgressChangedEventHandler(this.backgroundWorker_ProgressChanged);
     

public void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
   //roep code aan
   //notify iets naar je UI
   backgroundWorker.ReportProgress(0, "Mijn message of zoiets");
}
public void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs 
{
       GUI.Label13.Text =  (String) e.UserState;
}
public void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
        // doe dingen om je component op te ruimen, indien van toepassing
}

//Alternatief kun je natuurlijk ook de backgroundworker overriden en je eigen specifieke 
//implementatie in verwerken zodat je al deze code niet in je UI hebt staan. Dan krijg je zoiets als dit.

MijnWorker w = new MijnWorker(MijnLabel);

public MijnWorker(Label label)
{
  //events knopen
  //en dan in progresschanged event je message aan je label knopen
}

[ Voor 88% gewijzigd door D-Raven op 06-10-2008 09:25 ]


Acties:
  • 0 Henk 'm!

  • whoami
  • Registratie: December 2000
  • Laatst online: 01:56
Ik snap ook niet wat je moeilijk vind aan de backgroundworker ?
Die component maakt het je nl. heel wat makkelijker, aangezien hij (de backgroundworker dus), al abstractie maakt van het al of niet invoken. Daar hoef jij je dus niets van aan te trekken.
(In het kort: een UI control mag slechts gewijzigd worden door de thread die die control heeft aangemaakt. Als jij dus vanaf een andere thread bv een progressbar wilt updaten, dan moet je ervoor zorgen dat je dat op de juiste thread doet, vandaar : checken op invokeRequired en indien nodig invoken. Dit zorgt voor een 'context-switch').

Of het een 'best practice' is om die BGW te gebruiken is weer iets anders; hij maakt het je in sommige gevallen makkelijker , dat wel.

https://fgheysels.github.io/


Acties:
  • 0 Henk 'm!

  • shades
  • Registratie: September 2001
  • Laatst online: 14-07 13:45
whoami schreef op maandag 06 oktober 2008 @ 10:10:
Ik snap ook niet wat je moeilijk vind aan de backgroundworker ?
Die component maakt het je nl. heel wat makkelijker, aangezien hij (de backgroundworker dus), al abstractie maakt van het al of niet invoken. Daar hoef jij je dus niets van aan te trekken.
(In het kort: een UI control mag slechts gewijzigd worden door de thread die die control heeft aangemaakt. Als jij dus vanaf een andere thread bv een progressbar wilt updaten, dan moet je ervoor zorgen dat je dat op de juiste thread doet, vandaar : checken op invokeRequired en indien nodig invoken. Dit zorgt voor een 'context-switch').

Of het een 'best practice' is om die BGW te gebruiken is weer iets anders; hij maakt het je in sommige gevallen makkelijker , dat wel.
Ben net aan het stoeien met verschillende kleine componentjes die ik net even heb gemaakt. De bgw valt inderdaad wel. Ik post vanmiddag wel even een testappje. Kunnen jullie kijken of het wel allemaal oke is :)

https://k1600gt.nl


Acties:
  • 0 Henk 'm!

  • shades
  • Registratie: September 2001
  • Laatst online: 14-07 13:45
Ik heb een testprogrammaatje in elkaar geflanst. Het werkt maar of ik alles ook op de juiste manier gebruikt is kan ik niet zo goed beoordelen omdat ik helemaal nieuw ben op dit terein.

Hier het vs2008 project

Ik misbruik volgens mij de ProgressPercentage-property van de ProgressChangedEventArgs op een gruwelijke manier om bij diverse events ander controls te wijzigen. Opbouwende kritiek welkom..

https://k1600gt.nl


Acties:
  • 0 Henk 'm!

  • whoami
  • Registratie: December 2000
  • Laatst online: 01:56
Laat ons zeggen dat het behoorlijk ranzig is ....
Ik bedoel maar: je bouwt zo een dependency in. Je gaat ervan uit dat je client moet weten dat die ProgressPrecentage een bepaalde 'magic value' heeft, etc...

Als je wat 'meer' wilt hebben dan enkel de gewone ProgressChanged etc... dan zou ik geen BackgroundWorker gebruiken, maar m'n eigen class schrijven.
Je zou ook kunnen de UserState property gebruiken van de ProgressChangedEventArgs om wat extra informatie door te geven naar je client, waarmee je dan wel proper kunt checken wat er precies moet gebeuren (welke textbox je wilt udaten bv).

https://fgheysels.github.io/


Acties:
  • 0 Henk 'm!

  • shades
  • Registratie: September 2001
  • Laatst online: 14-07 13:45
Was ik al bang voor ja maar dat vertelde ik al :o
Je zei al iets over "best practice" :/

Ik zou het zelf ook nooooit zo maken natuurlijk O-) (spreek mezelf hier gelijk heel erg tegen gezien het voorbeeld - moet het maar snel verwijderen). Gelukkig hoef ik het ook niet zelf te maken want die component bestaat al. Weet wel gelijk hoe het vooral niet moet. Das ook belangrijk. Moet je maar snel vergeten dat testje...

[ Voor 72% gewijzigd door shades op 06-10-2008 23:44 . Reden: aanvulling ]

https://k1600gt.nl

Pagina: 1