Toon posts:

[.NET] Cancelen van een intensieve berekening

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Hallo allemaal!

Eventjes een korte schets van de situatie:
Een mdicontainer met een aantal aanroepbare methoden;
In deze mdicontainer kan je andere vensters openen en op 1 van deze vensters kan je een methode aanroepen die zeer cpu-intensief werkt. Deze methode roept tijdens de werking eveneens die aanroepbare methoden van de mdicontainer op. Nu is het zo dat, indien de gebruiker dit wil, het intensieve proces moet kunnen onderbroken worden. Indien ik nu op het knopje Cancel klikt, dan reageert de applicatie er niet op vermits hij bezig is met die intensieve methode.
Hoe zou ik dit het beste oplossen?

Ik heb gedacht om te die intesieve methode te laten werken met in BackgroundWorker, maar deze aparte thread kan uiteraard niet aan de verschillende methoden en variabelen uit de mainthread, terwijl dit echt wel noodzakelijk is. Alle nodige variabelen meegeven is geen optie, omdat het er te veel zijn. Maar ben ik wel in de juiste richting aan het denken?

Alvast bedankt

edit:

vergeten bij de vermelden: .NET 3.5 in c#

[ Voor 3% gewijzigd door Verwijderd op 14-04-2008 15:33 ]


Acties:
  • 0 Henk 'm!

  • asfaloth_arwen
  • Registratie: Februari 2005
  • Laatst online: 19:22
Je zult toch echt moeten werken met een tweede thread, en een gedeelde variabele die gecontroleerd wordt door de CPU-intensieve thread.

Bijv. een volatile boolean die gezet word door background thread indien afgebroken moet worden, de andere thread kan dan zelf het werkelijke afbreken voltooien.

[ Voor 38% gewijzigd door asfaloth_arwen op 14-04-2008 15:38 ]

Specs


Acties:
  • 0 Henk 'm!

  • Sebazzz
  • Registratie: September 2006
  • Laatst online: 20:50

Sebazzz

3dp

Je kan gewoon met een backgroundworker werken hoor :)

Pseudocode:
C#:
1
2
3
4
5
6
7
class blaat {
string naam = 'piet';

public blaat(string naam) { this.naam = naam; }
public backgroundworker_dowork(event e) { this.naam = this.naam.replace('t', 's'); }

}


Als het gaat om win32 variabelen moet je thread safe calls maken. Dat doe je met delegate methods. Niet zo moeilijk, zie MSDN.

[Te koop: 3D printers] [Website] Agile tools: [Return: retrospectives] [Pokertime: planning poker]


Acties:
  • 0 Henk 'm!

  • whoami
  • Registratie: December 2000
  • Laatst online: 22:37
Verwijderd schreef op maandag 14 april 2008 @ 15:32:
Indien ik nu op het knopje Cancel klikt, dan reageert de applicatie er niet op vermits hij bezig is met die intensieve methode.
Hoe zou ik dit het beste oplossen?
Gebruik maken van een aparte thread / BackgroundWorker.
Indien de gebruiker wil cancelen, zet je een variable 'cancel' op true bv, en in je operatie check je geregeld of cancel true is of niet.
(Background worker heeft al de CancelAsync method en de CancellationPending property)
Ik heb gedacht om te die intesieve methode te laten werken met in BackgroundWorker, maar deze aparte thread kan uiteraard niet aan de verschillende methoden en variabelen uit de mainthread, terwijl dit echt wel noodzakelijk is. Alle nodige variabelen meegeven is geen optie, omdat het er te veel zijn. Maar ben ik wel in de juiste richting aan het denken?
Tja.... Je backgroundworker kan een method uitvoeren die een member is van de huidige class toch ?
Hoedanook, je zal er zeker moeten voor zorgen dat je geen racing toestanden hebt, etc... Je zal dus duidelijk moeten locken waar het nodig is, de locks niet onnodig lang laten zijn, etc...
Nu, ik weet niet wat je eisen precies zijn, of wat je precies wil doen, maar ik zou -als je operatie een resultaat moet berekenen van de input die het krijgt- gewoon alle nodige variablen meegegeven aan die taak.

[ Voor 8% gewijzigd door whoami op 14-04-2008 15:42 ]

https://fgheysels.github.io/


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Ik heb nu bv in de form een listview en deze code:
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
private void DoIntensief(){
listViewTable.Items[t.Text].ImageIndex = 1;
listViewTable.Items[t.Text].SubItems.Add("Processing...");
listViewTable.Update();
}

public backgroundworker_dowork(event e) {
 DoIntensief();
}

private void btnStart_Click(object sender, EventArgs e)
        {
backgroundWorker.RunWorkerAsync();
}


bij het klikken op de knop, moet het proces starten natuurlijk :)
Dan krijg ik een cross-thread exception dat hij listViewTable wil gebruiken die in een andere thread zit.

[ Voor 7% gewijzigd door Verwijderd op 14-04-2008 15:50 ]


Acties:
  • 0 Henk 'm!

  • user109731
  • Registratie: Maart 2004
  • Niet online
Verwijderd schreef op maandag 14 april 2008 @ 15:48:
Dan krijg ik een cross-thread exception dat hij listViewTable wil gebruiken die in een andere thread zit.
De tweede thread mag niet de UI wijzigen, je moet dus gebruik maken van Invoke of de waarde teruggeven aan de eerste thread zodat deze de GUI kan wijzigen. Daar is genoeg over te vinden denk ik...

Acties:
  • 0 Henk 'm!

Verwijderd

De foutmelding popup die Visual Studio toont geeft als het goed is al een link naar de nodige informatie om de applicatie thread-safe te maken.

Je zou het ook smerig kunnen oplossen door de check naar Illegal cross-thread calls uit te zetten.
Hoe dat moet ga ik niet zeggen omdat het smerig is :). Kun je rare fouten mee krijgen namelijk, omdat de app niet meer thread-safe is.

Acties:
  • 0 Henk 'm!

  • Hydra
  • Registratie: September 2000
  • Laatst online: 21-08 17:09
JanDM schreef op maandag 14 april 2008 @ 15:54:
De tweede thread mag niet de UI wijzigen, je moet dus gebruik maken van Invoke of de waarde teruggeven aan de eerste thread zodat deze de GUI kan wijzigen. Daar is genoeg over te vinden denk ik...
Als een worker thread die berekeningen doet op data je GUI moet benaderen, zit mijns inziens je ontwerp niet goed in mekaar.

Wikipedia: Model-View-Controller-model

https://niels.nu


Acties:
  • 0 Henk 'm!

  • Gerco
  • Registratie: Mei 2000
  • Laatst online: 19:06

Gerco

Professional Newbie

Hydra schreef op dinsdag 15 april 2008 @ 10:30:
Als een worker thread die berekeningen doet op data je GUI moet benaderen, zit mijns inziens je ontwerp niet goed in mekaar.
Threading heeft niet zoveel te maken met MVC. Als je worker thread je Model update, zal dat toch de update aan de Controller willen doorgeven. *iemand* moet die Invoke() doen, want anders gebeuren al die calls op je worker thread.

De logische plaats ervoor is in de worker thread, want je wilt je Model niet lastig vallen met threading code. De volgende logische plaats is dan, mijns inziens, de Controller. Die weet namelijk dat er een View is en dat dat GUI is wat op een andere thread draait.

@Hieronder: Eens, maar dat heeft weer niets met threading te maken :)

[ Voor 15% gewijzigd door Gerco op 15-04-2008 11:25 ]

- "Als ik zou willen dat je het begreep, legde ik het wel beter uit!" | All number systems are base 10!


Acties:
  • 0 Henk 'm!

  • Janoz
  • Registratie: Oktober 2000
  • Laatst online: 21-09 02:21

Janoz

Moderator Devschuur®

!litemod

Of MVC de oplossing is is inderdaad de vraag, maar als je business logic data rechtstreeks moet uitvragen uit de GUI dan is er toch wel degelijk wat mis met je ontwerp.

Ken Thompson's famous line from V6 UNIX is equaly applicable to this post:
'You are not expected to understand this'


Acties:
  • 0 Henk 'm!

  • Hydra
  • Registratie: September 2000
  • Laatst online: 21-08 17:09
Gerco schreef op dinsdag 15 april 2008 @ 11:09:
Threading heeft niet zoveel te maken met MVC. Als je worker thread je Model update, zal dat toch de update aan de Controller willen doorgeven. *iemand* moet die Invoke() doen, want anders gebeuren al die calls op je worker thread.
De tweede thread mag niet de UI wijzigen, je moet dus gebruik maken van Invoke of de waarde teruggeven aan de eerste thread zodat deze de GUI kan wijzigen
Je workerthread wijzigt in het MVC model sowieso je GUI niet. Als je klaar bent vuur je een event af. Je workerthread heeft 0.0 weet van je GUI.
Janoz schreef op dinsdag 15 april 2008 @ 11:16:
Of MVC de oplossing is is inderdaad de vraag, maar als je business logic data rechtstreeks moet uitvragen uit de GUI dan is er toch wel degelijk wat mis met je ontwerp.
MVC is geen 'oplossing', het is een manier van ontwerpen.

[ Voor 20% gewijzigd door Hydra op 15-04-2008 13:17 ]

https://niels.nu


Acties:
  • 0 Henk 'm!

  • Janoz
  • Registratie: Oktober 2000
  • Laatst online: 21-09 02:21

Janoz

Moderator Devschuur®

!litemod

Ik ken MVC (alhoewel vaak PAC wordt tegepast en mensen dit MVC noemen), maar ik vroeg me puur af of dit het acute probleem van de topicstarter op zou lossen. Er zijn meer manieren om je presentatie van je business logic te scheiden.

Ken Thompson's famous line from V6 UNIX is equaly applicable to this post:
'You are not expected to understand this'


Acties:
  • 0 Henk 'm!

  • bigbeng
  • Registratie: Augustus 2000
  • Laatst online: 26-11-2021
Dit klinkt inderdaad meer als observer/observable pattern.

En deze thread, is dat iets voor jou?
\[C#]Events, Threads en delegates
edit:
MVC gebruikt trouwens vaak het Observer/Observable pattern

[ Voor 18% gewijzigd door bigbeng op 15-04-2008 13:39 ]


Acties:
  • 0 Henk 'm!

  • Gerco
  • Registratie: Mei 2000
  • Laatst online: 19:06

Gerco

Professional Newbie

Hydra schreef op dinsdag 15 april 2008 @ 13:15:
Je workerthread wijzigt in het MVC model sowieso je GUI niet. Als je klaar bent vuur je een event af. Je workerthread heeft 0.0 weet van je GUI.
Dan moet je dat event nog steeds ergens naar de GUI thread marshallen. Of je nu wel of geen MVC gebruikt is hier niet van belang. Overigens zei ik al dat je Worker de Model moet updaten, niet de GUI.

[ Voor 12% gewijzigd door Gerco op 15-04-2008 15:24 ]

- "Als ik zou willen dat je het begreep, legde ik het wel beter uit!" | All number systems are base 10!


Acties:
  • 0 Henk 'm!

  • MrSleeves
  • Registratie: Februari 2004
  • Laatst online: 24-08 23:48

MrSleeves

You'll thank me later.

Als je je GUI wilt updaten kan je dit het beste met BackgroundWorker.ReportProgress doen, en dan het ProgressChanged event afvangen.

30Drie Web Design & IT Consultancy | Raven Consultancy Services


Acties:
  • 0 Henk 'm!

  • Hydra
  • Registratie: September 2000
  • Laatst online: 21-08 17:09
Gerco schreef op dinsdag 15 april 2008 @ 15:23:
[...]

Dan moet je dat event nog steeds ergens naar de GUI thread marshallen. Of je nu wel of geen MVC gebruikt is hier niet van belang. Overigens zei ik al dat je Worker de Model moet updaten, niet de GUI.
Je stelt dat je geen 'invoke()' code in je Model wilt hebben, dat klopt. Maar je worker thread heeft dat ook niet nodig. In het MVC model laat je je model een event afgeven op het moment dat 'ie gewijzigd is. Je gaat sowieso nergens in je model een invoke() naar je GUI laten doen. Je model (en dus ook je workerthread in de beschreven situatie) hebben 0 weet van je GUI laag.

https://niels.nu


Acties:
  • 0 Henk 'm!

  • Gerco
  • Registratie: Mei 2000
  • Laatst online: 19:06

Gerco

Professional Newbie

Hydra schreef op dinsdag 15 april 2008 @ 15:34:
Je model (en dus ook je workerthread in de beschreven situatie) hebben 0 weet van je GUI laag.
Klopt, maar je Worker weet wel dat hij een workerthread is en dus dat hij een invoke() moet doen. De andere mogelijke partij is de Controller. Die weet dat er een GUI laag is en dat er geinvoke()d dient te worden. Scheelt weer invokes wanneer er geen GUI aanwezig is.

Een event gaat niet opeens op magische wijze over naar een andere thread, niet in alle omgevingen en runtimes in ieder geval. Kan best zijn dat dat in .NET wel zo is, daarvoor weet ik niet genoeg van .NET.

[ Voor 18% gewijzigd door Gerco op 15-04-2008 15:37 ]

- "Als ik zou willen dat je het begreep, legde ik het wel beter uit!" | All number systems are base 10!


Acties:
  • 0 Henk 'm!

  • Hydra
  • Registratie: September 2000
  • Laatst online: 21-08 17:09
Gerco schreef op dinsdag 15 april 2008 @ 15:36:
Klopt, maar je Worker weet wel dat hij een workerthread is en dus dat hij een invoke() moet doen.
Ik denk dat we beiden een andere opvatting over het begrip workerthread hebben. Een workerthread voor mij is een thread die langdurige berekeningen uitvoert en als hij daar mee klaar is een "ModelChanged" event afvuurt. V.z.i.w. hoef je een .Net of Java event niet te marshallen, in Java heb je sowieso een eventhandler thread die deze zaken afhandelt.

https://niels.nu


Acties:
  • 0 Henk 'm!

  • D-Raven
  • Registratie: November 2001
  • Laatst online: 10-09 20:32
In .Net wordt een event niet automatisch gemarshalled. Behalve als je gebruik maakt van het BackgroundWorker component. Indien je een event handler koppeld aan een event welke in een andere thread opgegooid wordt dan zul je in je event handler wel degelijk invoke moeten gebruiken.

Acties:
  • 0 Henk 'm!

  • EfBe
  • Registratie: Januari 2000
  • Niet online
D-Raven schreef op woensdag 16 april 2008 @ 15:27:
In .Net wordt een event niet automatisch gemarshalled. Behalve als je gebruik maakt van het BackgroundWorker component. Indien je een event handler koppeld aan een event welke in een andere thread opgegooid wordt dan zul je in je event handler wel degelijk invoke moeten gebruiken.
Een eventhandler wordt altijd al met Invoke gecalled (het is een delegate tenslotte), dus wat jij zegt gebeurt al.

overigens helpen events niet, de message die wordt gezonden door de button click wordt niet afgehandeld door winforms tenzij Application.DoEvents() wordt aangeroepen op de main thread tijdens een operatie. Dus echt een aparte thread met het werk, en met thread synchronization technieken laat je die dan met de foreground thread praten (die staat te wachten op input)

[ Voor 26% gewijzigd door EfBe op 16-04-2008 15:48 ]

Creator of: LLBLGen Pro | Camera mods for games
Photography portfolio: https://fransbouma.com


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Hohoho, zo een discussie over een vraag van mij, dit was nu ook niet echt de bedoeling jongens :P

Het zou mij niet verbazen dat er iets mis is aan mijn ontwerp, maar al doende leert men, niet waar?
In de toekomst weet ik nu dat ik zo een systeem niet meer moet gebruiken, maar iets beter moet nadenken over het ontwerp. Ik zal jullie aanbevelingen hier eens rustig bekijken en hopen dat ik op een oplossing uitkom!

Acties:
  • 0 Henk 'm!

  • pkuppens
  • Registratie: Juni 2007
  • Laatst online: 21:55
Verwijderd schreef op donderdag 17 april 2008 @ 09:55:
Hohoho, zo een discussie over een vraag van mij, dit was nu ook niet echt de bedoeling jongens :P

Het zou mij niet verbazen dat er iets mis is aan mijn ontwerp, maar al doende leert men, niet waar?
In de toekomst weet ik nu dat ik zo een systeem niet meer moet gebruiken, maar iets beter moet nadenken over het ontwerp. Ik zal jullie aanbevelingen hier eens rustig bekijken en hopen dat ik op een oplossing uitkom!
Al doende leert men.... Oei, moeilijk om hier niet op te reageren.

Een van de grondgedachten bij Design Patterns e.d. is dat je beter kunt leren van de successen van anderen dan van je eigen fouten...

Aan de andere kant, enkele maanden of jaren investeren om Design Patterns een beetje te begrijpen en kunnen gebruiken, voordat je je probleem kunt tacklen, is ook met een kanon op een mug schieten.

Acties:
  • 0 Henk 'm!

  • D-Raven
  • Registratie: November 2001
  • Laatst online: 10-09 20:32
EfBe schreef op woensdag 16 april 2008 @ 15:44:
[...]

Een eventhandler wordt altijd al met Invoke gecalled (het is een delegate tenslotte), dus wat jij zegt gebeurt al.
Ok nu wordt het verwarrend. Wat ik bedoel is dat je eventhandler altijd wordt uitgevoerd in dezelfde thread als waar het event wordt opgegooid (waar of niet?) en dat je dan dus zelf invoke zal moeten gebruiken wil je de executie van je eventhandler naar een andere thread halen.

Wat jij bedoeld is dat als je een eventhandler gekoppeld hebt aan een event, welke wordt opgegooid in een andere thread dat .net dan automatisch deze handler naar de thread van het event marshalled, of begrijp ik nu iets verkeerd. 8)7

Acties:
  • 0 Henk 'm!

  • Hydra
  • Registratie: September 2000
  • Laatst online: 21-08 17:09
D-Raven schreef op vrijdag 18 april 2008 @ 09:57:
Ok nu wordt het verwarrend. Wat ik bedoel is dat je eventhandler altijd wordt uitgevoerd in dezelfde thread als waar het event wordt opgegooid (waar of niet?)
Dat is in ieder geval in het geval van Java niet zo. Daar heb je een aparte eventhandler thread.

https://niels.nu


Acties:
  • 0 Henk 'm!

  • MrSleeves
  • Registratie: Februari 2004
  • Laatst online: 24-08 23:48

MrSleeves

You'll thank me later.

Is dat niet de reden van alle On.... subs die je kan overerven en waar uiteindelijk het event wordt opgegooid?
In de Backgroundworker kan je ReportProgress aanroepen, die roept OnProgressChanged aan, die vervolgens de event opgooid. Ik neem aan dat die OnProgressChanged de event opgooid in de thread van de parent van de BackgroundWorker. Of iets in die richting in ieder geval.

30Drie Web Design & IT Consultancy | Raven Consultancy Services


Acties:
  • 0 Henk 'm!

Verwijderd

Verwijderd schreef op maandag 14 april 2008 @ 15:48:
Ik heb nu bv in de form een listview en deze code:
C#:
1
2
3
4
5
private void DoIntensief(){
listViewTable.Items[t.Text].ImageIndex = 1;
listViewTable.Items[t.Text].SubItems.Add("Processing...");
listViewTable.Update();
}
verander het stukje code van jou hierboven in:

C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
delegate void DoIntensiefDelegate();

private void DoIntensief()
{
    if ( listViewTable.InvokeRequired )
    {
        DoIntensiefDelegate d = new DoIntensiefDelegate( DoIntensief() );
        this.Invoke( d );
    }
    else
    {
        listViewTable.Items[ t.Text ].ImageIndex = 1;
        listViewTable.Items[ t.Text ].SubItems.Add( "Processing..." );
        listViewTable.Update();
    }
}


have fun!

zie ook: MSDN

[ Voor 5% gewijzigd door Verwijderd op 18-04-2008 10:51 . Reden: Update met msdn link :) ]


Acties:
  • 0 Henk 'm!

  • MrSleeves
  • Registratie: Februari 2004
  • Laatst online: 24-08 23:48

MrSleeves

You'll thank me later.

Kan, maar ik zou toch het intensieve gedeelte in een Backgroundworker class laten lopen.
Op het moment dat de GUI geupdate moet kunnen worden, roep je de ReportProgress aan. En de listViewTable aanroepen zet je dan in de ProgressChanged handler.
Op die manier kan je ook vrij makkelijk een Progressbar laten lopen.

30Drie Web Design & IT Consultancy | Raven Consultancy Services

Pagina: 1