[.NET] Updaten via UI verloopt schokkerig ondanks threading

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

  • Knakker
  • Registratie: April 2000
  • Laatst online: 26-07 21:05
In een van mijn programma's voer ik een statistische simulatie uit. Die simulatie wordt simultaan uitgevoerd over meerdere threads om het proces zo snel mogelijk te laten verlopen. De voortgang van de berekening (die lang kan duren) wordt weergeven in de UI thread; het 'probleem' is echter dat het updaten daarvan schokkerig verloopt, terwijl de UI thread verder echt niets te doen heeft (of beter gezegd: zou moeten hebben...?). Ik vermoed dat ik dus toch wat over het hoofd zie, en ik wil graag weten hoe dat komt :)

Ik heb een klasse Progress in mijn 'domein' laag zitten waar de afzonderlijke threads hun voortgang aan rapporteren. Progress heeft op dit moment slechts een maximum, een countertje (bij iedere voltooide iteratie wordt het countertje één verhoogd) en een status (inactive, waiting, working, killed, finished, etc).

Op de UI thread start ik een DialogBox. Die maakt twee threads aan: één thread Work voor het starten van de berekening (die op haar beurt weer de berekening weer opsplitst in een x aantal threads) en één thread Status die verantwoordelijk is voor het checken van de voortgang van de berekening. Thread Status doet niets anders dan iedere 10ms kijken wat het huidige aantal voltooide iteraties is en, als die gewijzigd is, de DialogBox haar progressbar laten updaten. De iteraties verlopen dusdanig snel dat er minstens 500 iteraties verwerkt worden in een seconde, dus iedere 10ms is er voldoende voortgang weer te geven.

Als ik één model doorreken (twee threads, één op elke core) dan verloopt het updaten regelmatig - althans, zo lijkt het. Als ik meer modellen doorreken (2 threads voor elk model) dan verloopt het updaten schokkerig. Dat snap ik echter niet, omdat ik slechts iedere 10ms een update uitvoer van de progressbar -los van hoe vaak de verschillende threads klasse Progress bijwerken- en de UI thread heeft volgens mij verder niets te doen. Het gebeurt nu dat de balk soms seconden stil blijft staan, om dan ineens 25% te verspringen.

Wat zie ik over het hoofd? Alvast bedankt :)

Geef mij maar een Warsteiner.


Acties:
  • 0 Henk 'm!

  • IceM
  • Registratie: Juni 2003
  • Laatst online: 11-09 20:35
Als je de worker threads een lagere prioritijd geeft, werkt het dan wel goed?

...


Acties:
  • 0 Henk 'm!

  • Razr
  • Registratie: September 2005
  • Niet online
Misschien dat het te maken heeft met wanneer je form/control opnieuw getekend wordt? Probeer dat eens te forceren na een update aan je voortgang indicator.

Acties:
  • 0 Henk 'm!

  • creator1988
  • Registratie: Januari 2007
  • Laatst online: 11-09 14:44
Waarom elke 10 ms, dat is toch achterlijk veel updates/sec. Waarom niet eens per 100 ms oid? Verder vermoed ik ook iets met de prio; als je cores helemaal vol belast worden zal hij wat meer moeite hebben je main thread goed responsief te houden.

Acties:
  • 0 Henk 'm!

  • Phyxion
  • Registratie: April 2004
  • Niet online

Phyxion

_/-\o_

10 ms is wel 100x per seconde, aangezien de draw nogal erg traag is kan je dit beter wat lager zetten (De draw trekt dit lang niet).

'You like a gay cowboy and you look like a gay terrorist.' - James May


Acties:
  • 0 Henk 'm!

  • roy-t
  • Registratie: Oktober 2004
  • Laatst online: 08-09 11:33
Misschien kun je beter elke keer als je progress iets nieuws rapporteert er voor zorgen dat je control invalidate zodat windows/.net er zelf voor kan kiezen om het opnieuw te tekenen. En misschien hoef hij dan ook niet alles te tekenen.

Dus probeer eens control.Invalidate(); als je progress wat nieuws te melden heeft :).

~ Mijn prog blog!


  • Ruudjah
  • Registratie: November 1999
  • Laatst online: 06-09 20:58

Ruudjah

2022

Soortgelijk probleem ook eens gehad. Toen opgelost door in de UI thread een call te doen naar een methode waar ik de naam vergeten van ben. Was zoals ^^ ook iets als Update() of Paint(). Na deze call verliep het updateten van de UI wel soepeltjes.

TweakBlog


  • Down
  • Registratie: Februari 2005
  • Laatst online: 23:01
Ruudjah schreef op donderdag 12 november 2009 @ 08:56:
Soortgelijk probleem ook eens gehad. Toen opgelost door in de UI thread een call te doen naar een methode waar ik de naam vergeten van ben. Was zoals ^^ ook iets als Update() of Paint(). Na deze call verliep het updateten van de UI wel soepeltjes.
Application.DoEvents() :+

Mother north, how can they sleep while their beds are burning?


  • D-Raven
  • Registratie: November 2001
  • Laatst online: 10-09 20:32
Als je echt je UI handmatig wilt gaan verversen, doe dat dan idd met mijnControl.Invalidate. Waar mijnControl de control is waarvan je de inhoud hebt bijgewerkt.
Maargoed, als het een Label is dan gebeurd dat automatisch op het moment dat je de Text property set. Same goes voor de Increment of PerformStep methode op de ProgressBar.

Beter is om het aantal updates per seconde terug te brengen naar 10 ofzo. Dat is nog steeds snel genoeg om op een mooie manier je progressbar te animeren en geeft je UI meer tijd om zich te updaten.

En Application.DoEvents() is evil en wordt imho alleen gebruikt als oplossing als je geen idee hebt waar je mee bezig bent.

  • Down
  • Registratie: Februari 2005
  • Laatst online: 23:01
D-Raven schreef op donderdag 12 november 2009 @ 11:48:
En Application.DoEvents() is evil en wordt imho alleen gebruikt als oplossing als je geen idee hebt waar je mee bezig bent.
Wat een onzin zeg. Als je je maar bewust bent van het feit dat Application.DoEvents alle messages in de message queue verwerkt en niet alleen die van je controls (en daar rekening mee houdt).

Ik zeg overigens niet dat het in dit geval verstandig is, ik gokte enkel de naam van de functie die Ruudjah vergeten was.

Mother north, how can they sleep while their beds are burning?


  • R4gnax
  • Registratie: Maart 2009
  • Laatst online: 06-09 17:51
D-Raven schreef op donderdag 12 november 2009 @ 11:48:En Application.DoEvents() is evil en wordt imho alleen gebruikt als oplossing als je geen idee hebt waar je mee bezig bent.
... Of wanneer je wel degelijk weet waar je mee bezig bent en je eigen message loop draait. Bijvoorbeeld wanneer je een videogame o.i.d. aan het maken bent en feitelijk niet veel meer van Windows.Forms wilt dan een kaal venster waarop je een DirectX of OpenGL context kunt aanmaken.

  • D-Raven
  • Registratie: November 2001
  • Laatst online: 10-09 20:32
Down schreef op donderdag 12 november 2009 @ 13:35:
[...]


Wat een onzin zeg. Als je je maar bewust bent van het feit dat Application.DoEvents alle messages in de message queue verwerkt en niet alleen die van je controls (en daar rekening mee houdt).

Ik zeg overigens niet dat het in dit geval verstandig is, ik gokte enkel de naam van de functie die Ruudjah vergeten was.
Ben ik niet mee eens. Overigens was mijn opmerking niet bedoeld als een steek richting jou persoonlijk. Als je Application.DoEvents moet gebruiken dan betekend dat dat je ergens iets goed fout aan het doen bent. Per definitie houdt het in dat je de UI Thread aan het doodspammen bent of dat je je UI communicatie op een verkeerde wijze geïmplementeerd hebt. Als je dan vervolgens niet kunt vaststellen waar dat probleem zich dan bevindt. Dan weet je imho niet waar je nu daadwerkelijk mee bezig bent.

  • D-Raven
  • Registratie: November 2001
  • Laatst online: 10-09 20:32
R4gnax schreef op donderdag 12 november 2009 @ 13:49:
[...]


... Of wanneer je wel degelijk weet waar je mee bezig bent en je eigen message loop draait. Bijvoorbeeld wanneer je een videogame o.i.d. aan het maken bent en feitelijk niet veel meer van Windows.Forms wilt dan een kaal venster waarop je een DirectX of OpenGL context kunt aanmaken.
Ja ok. Maar dan ben je Windows Forms voor iets aan het gebruiken waar het eigenlijk niet voor bedoeld is. Er zijn legio .net alternatieven die zich daar veel beter voor lenen. XNA om er maar 1 te noemen.

  • boe2
  • Registratie: November 2002
  • Niet online

boe2

'-')/

Application.DoEvents() kan onvoorspelbaar zijn in multithreaded applicaties. Ben er zelf ook geen fan van. Single threaded nog nooit problemen mee tegengekomen echter.

[ Voor 22% gewijzigd door boe2 op 12-11-2009 13:52 ]

'Multiple exclamation marks,' he went on, shaking his head, 'are a sure sign of a diseased mind.' - Pratchett.


  • Down
  • Registratie: Februari 2005
  • Laatst online: 23:01
D-Raven schreef op donderdag 12 november 2009 @ 13:49:
[...]
Per definitie houdt het in dat je de UI Thread aan het doodspammen bent of dat je je UI communicatie op een verkeerde wijze geïmplementeerd hebt.
Niet mee eens, je stelt het veel te zwart-wit. Net zoals eval en thread.sleep niet per definitie 'evil' zijn. Feit is dat threading soms buitengewoon lastig te realiseren is. Als delegates simpelweg genoeg zijn zou ik ook daar voor kiezen. Maar soms is de afweging tussen een simpel model met Application.DoEvents en een complex threading model dat nog veel foutgevoeliger is niet zo eenduidig.

Mother north, how can they sleep while their beds are burning?


  • D-Raven
  • Registratie: November 2001
  • Laatst online: 10-09 20:32
Down schreef op donderdag 12 november 2009 @ 14:24:
[...]


Niet mee eens, je stelt het veel te zwart-wit. Net zoals eval en thread.sleep niet per definitie 'evil' zijn. Feit is dat threading soms buitengewoon lastig te realiseren is. Als delegates simpelweg genoeg zijn zou ik ook daar voor kiezen. Maar soms is de afweging tussen een simpel model met Application.DoEvents en een complex threading model dat nog veel foutgevoeliger is niet zo eenduidig.
Ik zie het inderdaad heel zwart wit.
Om iets multithreaded te maken kan inderdaad heel lastig zijn. Maar dan heb je het over het ontwerpen van je achterliggend model. Het bouwen van een laag die zorgt voor de communicatie tussen je model en je UI staat gelijk aan het bouwen van een laag waarin problemen worden opgelost die allemaal al eens ergens beschreven of uitgewerkt zijn.
Er zijn talloze artikelen over te vinden. Daarom zie ik geen enkele reden waarom je bij normaal gebruik van het winforms framework met Application.DoEvents aan de slag moet.

Als je Application.DoEvents gebruikt dan ben je of niet op de hoogte van alle mogelijkheden van het .net framework omtrent Threading. Of je bent gewoon aan het prutsen :P.


edit:

offtopic:
Maargoed het wordt zo voort een eigen discussie. Wil er best over verder ouwehoeren, maar wellicht handig om het dan naar een eigen topic te splitsen?

[ Voor 6% gewijzigd door D-Raven op 12-11-2009 15:14 ]


  • Evilbee
  • Registratie: November 2002
  • Laatst online: 11-09 11:46
D-Raven schreef op donderdag 12 november 2009 @ 11:48:
Beter is om het aantal updates per seconde terug te brengen naar 10 ofzo. Dat is nog steeds snel genoeg om op een mooie manier je progressbar te animeren en geeft je UI meer tijd om zich te updaten.
Nog beter is om helemaal niet te pollen, maar een event uit te sturen als de counter in de Progress class wordt aangepast en daar je form op laten listenen.

LinkedIn - Collega worden?


  • D-Raven
  • Registratie: November 2001
  • Laatst online: 10-09 20:32
Dat zou inderdaad beter zijn. Maar de counter in de Progress class wordt door verschillende threads per iteratie bijgewerkt. Het is dus niet zo dat dat de var is die zijn voortgang in percentages uitdrukt.

  • CodeIT
  • Registratie: Juni 2002
  • Laatst online: 09-09 22:04

CodeIT

Code IT

Evilbee schreef op donderdag 12 november 2009 @ 15:17:
[...]

Nog beter is om helemaal niet te pollen, maar een event uit te sturen als de counter in de Progress class wordt aangepast en daar je form op laten listenen.
Zoals ik het begrepen heb gebeurt dat nog vaker als de 100 keer per seconde. Het zal wel vloeiender gaan (thread wacht op invoke die de progressbar update) maar je levert dan wel veel performance in op je thread.

  • Down
  • Registratie: Februari 2005
  • Laatst online: 23:01
D-Raven schreef op donderdag 12 november 2009 @ 15:11:
[...]

Als je Application.DoEvents gebruikt dan ben je of niet op de hoogte van alle mogelijkheden van het .net framework omtrent Threading. Of je bent gewoon aan het prutsen :P.
Grote onzin. Ik ben prima op de hoogte van wat er mogelijk is met threading in .NET. Juist daarom weet ik dat de ontwikkeltijd en onderhoudbaarheid van sommige complexe threadingmechanieken niet opwegen tegen een simpelere constructie die precies hetzelfde werkt. Zolang je maar weet waar je mee bezig bent. Het gaat er niet om of iets wel of niet mogelijk zou zijn met de threading mogelijkheden in .NET. Het gaat ook de alternatieven en zaken zoals ontwikkeltijd en onderhoudbaarheid.

Zie ook eens dit artikel.

Mother north, how can they sleep while their beds are burning?


  • D-Raven
  • Registratie: November 2001
  • Laatst online: 10-09 20:32
Down schreef op donderdag 12 november 2009 @ 16:19:
[...]
Grote onzin. Ik ben prima op de hoogte van wat er mogelijk is met threading in .NET.
Ik heb ook nergens gezegd dat jij dat niet bent. Ik ventileer slechts mijn mening dat Application.DoEvents op zijn zacht's gezegd bad practise is.
Juist daarom weet ik dat de ontwikkeltijd en onderhoudbaarheid van sommige complexe threadingmechanieken niet opwegen tegen een simpelere constructie die precies hetzelfde werkt. Zolang je maar weet waar je mee bezig bent. Het gaat er niet om of iets wel of niet mogelijk zou zijn met de threading mogelijkheden in .NET. Het gaat ook de alternatieven zaken zoals ontwikkeltijd en onderhoudbaarheid.

Zie ook eens dit artikel.
Ik ga er vanuit dat ik voldoende tijd krijg om de kwaliteit van mijn werk te kunnen waarborgen. Op het moment dat dat niet zo is dan worden er "shortcuts" genomen. Application.DoEvents is een van die shortcuts. Maar dat wil nog niet zeggen dat ik er blij mee ben of dat dat mijn mening daarover veranderd. Natuurlijk moet je als ontwikkelaar afwegingen maken gebaseerd op de situatie waar je inzit.

Maar dat neemt niet weg dat als ik de tijd heb om dingen goed te doen, ik een alternatieve oplossing prefereer boven Application.DoEvents. Maargoed, dat ben ik.

Je link naar de blog van Jeff Atwood heeft niet zoveel waarde. Ik kan net zoveel (recentere) artikelen aandragen van mensen die mijn mening delen, maar wat heeft dat voor zin.

  • Reptile209
  • Registratie: Juni 2001
  • Nu online

Reptile209

- gers -

Zolang je GUI-thread niet zelf ook in een zware lus zit, zou DoEvents() volgens mij niks moeten toevoegen: je GUI zit dan al in een een loop te wachten op een event. Als de threads gewoon netjes van tijd tot tijd een event afvuren om hun status door te geven en niet op onderlinge locks zitten te wachten, zou het best soepel moeten lopen volgens mij. Dan kan je vervolgens de Status-thread weghalen, omdat je dat netjes bij het afhandelen van je events verwerkt.
Als je actief moet gaan pollen voor resultaten, dan vraag ik me af of je de boel wel handig opgezet hebt. Kan je niet wat (stukjes!) code posten hoe je nu te werk gaat?

Zo scherp als een voetbal!


  • Knakker
  • Registratie: April 2000
  • Laatst online: 26-07 21:05
Ontzettend bedankt voor alle reakties tot zover :)
Als je de worker threads een lagere prioritijd geeft, werkt het dan wel goed?
Nee, maakt niet uit.
Nog beter is om helemaal niet te pollen, maar een event uit te sturen als de counter in de Progress class wordt aangepast en daar je form op laten listenen.
Er worden rustig een paar honderd keer iteraties per seconde afgewerkt, dus dat lijkt me veel belastender dan een regelmatige poll (+ een stukje overhead van een slapende status thread). Of vergis ik me daarin?
Als je actief moet gaan pollen voor resultaten, dan vraag ik me af of je de boel wel handig opgezet hebt. Kan je niet wat (stukjes!) code posten hoe je nu te werk gaat?
Het is zo maar mogelijk dat dit helemaal niet de handigste manier is... ik ben primair een wiskundige en geen software ontwikkelaar ;) De berekeningen worden over meerdere threads uitgevoerd in de 'Domain' laag van de code, en ik zoek dus een manier om in de UI laag (en tevens UI thread) soepel de voortgang te tonen.

Het updaten van de bar verloopt nu via een event in de Progress klasse, maar het wordt er niet veel beter van... Iemand nog een idee?

[ Voor 8% gewijzigd door Knakker op 12-11-2009 20:29 ]

Geef mij maar een Warsteiner.


  • D-Raven
  • Registratie: November 2001
  • Laatst online: 10-09 20:32
Wat bedoel je met het wordt er niet veel beter van. Wat heb je dan veranderd ten opzicht van je situatie tijdens je eerste post.
Post anders een stuk van de code die je gebruikt om je UI te updaten. Dus je update thread en je Progress klasse. Dan kunnen wij wellicht iets concretere opmerkingen maken.

  • pedorus
  • Registratie: Januari 2008
  • Niet online
Wat veel reacties voordat de term BackgroundWorker valt. Die schijnt hiervoor gemaakt te zijn (even gesteld dat threads niet perse tegelijkertijd gestart moeten worden). :) Het lijkt me dat je niet een aparte status-Thread Progress wil, want dat is een soort busy-wait achtig systeem. Updates iedere 10ms lijken me ook niet heel wenselijk, want dat zijn er 100/sec en zo snel kijk je toch niet.

Vitamine D tekorten in Nederland | Dodelijk coronaforum gesloten

Pagina: 1