C#, evitare lo stallo di un'applicazione...

martedì 09 giugno 2009 - 14.51

phai Profilo | Newbie

Ciao a tutti,
sono nuovo sul forum.

Innanzitutto volevo farvi i complimenti: spesso tramite Google sono arrivato qui e quasi sempre ho trovato quello di cui avevo bisogno...

Così ho pensato di iscrivermi, magari per dare una mano a qualcuno, ma anche perché ora vorrei porre un quesito.

Sto sviluppando un'applicazione che, al click di un pulsante, elabori delle informazioni settate e crei all'interno di un TableLayoutPanel (a 2 colonne) un numero di TextBox variabile dalle 4 alle 24750... ognuno dei quali deve contenere un valore elaborato secondo determinati criteri.

E' chiaro come l'elaborazione di tutto questo può risultare particolarmente lunga, nonostante disabiliti la rappresentazione durante l'elaborazione (suspendLayout).

Quello che succede, quindi, è che l'applicazione venga considerata in "stallo" dal sistema operativo (nel task manager si parla di "l'applicazione non risponde" e la finestra diventa tutta bianca finché l'applicazione decide di riprendere vita) fino al termine delle elaborazioni.

Ho provato a risolvere il tutto usando un BackgroundWorker o, magari, a lanciare un Thread che visualizzi una finestra d'attesa (splash), ma le cose non cambiano.

Purtroppo l'applicazione è STA e pertanto dovrei essere in grado di mandare in background l'esecuzione di questo metodo (che accede comunque all'interfaccia grafica), mentre un altro elemento dell'interfaccia grafica rimanga attivo dicendo di attendere... o quantomeno mi basterebbe evitare che l'applicazione venga considerata bloccata (che non risponde) dal sistema operativo...

Grazie in anticipo per la disponibilità



Jeremy Profilo | Guru

Ciao PierPaolo.
Puoi mostrare il codice relativo alle prove che hai fatto con il BackGroundWorker??
A parer mio, la soluzione, sta nell'uso di un thread separato, quindi mi concentrerei su quello.....se non chè, la grafica del form, deve comunque essere gestita dal thread principale(quello di creazione del form) ... quindi bisognerebbe lavorarci un pò su.

Facci sapere...
ciao.

phai Profilo | Newbie

Ciao Jeremy e grazie per l'aiuto.

Mi spiace, ma il codice del background worker l'ho eliminato pensando non fosse la soluzione adatta.

Comunque ora sto facendo alcune prove di verifica. Nello specifico sto facendo creare "solo" 10 mila textbox e valutando i tempi di risposta... in questo modo sto effettuando anche del tuning sulle prestazioni... ma soprattutto sto dicendo all'applicazione di non effettuare alcun calcolo sul valore che le textbox dovrebbero contenere (le creo vuote insomma) per capire come stanno le cose.

Pertanto attualmente:

1. sul form principale clicco su un pulsante;
2. questo form dispone di un TablePane e, al click, in una sua cella dispongo un componente grafico customizzato che ho creato (lo istanzio e invoco su di lui il metodo per la creazione dei campi);
3. questo componente crea al suo interno un'ulteriore TablePane, ed esegue un ciclo che crea e dispone quei 10k campi nella varie celle;

Eppure per creare 10 mila campi in questo modo (su un XP con Dual Core a 2,10 e 2GB di Ram di cui 1 libera durante il debug) ci mette quasi 50 secondi... nei quali l'applicazione sembra "stallata"!!!

Dato che tu mi hai confermato che alla GUI può accedere solo il thread principale... allora il problema del BackgroundWorker sembra non porsi: il fatto è che per creare un numero così elevato di elementi grafici ci vuole del tempo... e io avrei bisogno che in quel tempo l'applicazione non sembrasse morta... ma solo in attesa...

Ancora grazie

Jeremy Profilo | Guru

Ciao PierPaolo.
Visto che ti basterebbe dimostrare all'utente che l'applicazione non si è inchiodata, ma è semplicemente al lavoro, potresti, come hai già accennato tu, visualizzare un form di attesa, magari con una progressbar che mostra lo stato dell'operazione.
A questo punto, lasci fare il lavoro più duro (la creazione dei controlli) al thread principale, e se si inchioda chi se ne frega, mentre, crei e gestisci il form di attesa su un thread separato.
Ad ogni modo, rimango a disposizione per ulteriori info.

Facci sapere...
Ciao.

phai Profilo | Newbie

Ciao e grazie mille per la risposta e il tempo che mi stai dedicando.

Il problema è cha anche in questo modo ho dei problemini non indifferenti.

Magari sbaglio qualcosa... io avevo fatto così:

1. definisco un nuovo thread che fa partire un metodo in cui istanzio una nuova finestra (componente Form) che attivo (.Show() e .Activate());
2. invoco il metodo che deve fare tutte le operazioni lunghe
3. killo il thread esterno

Il problema è che la finestra che avverte di attendere appare per un solo attimo e poi scompare subito... mentre il bel form principale con l'applicazione bloccata rimane!!! :(

Jeremy Profilo | Guru

Ciao PierPaolo.
Giusto per capirci .... guarda il progettino in allegato al post ...... una cosa del genere può risolvere la situazione???

Facci sapere...
Ciao

phai Profilo | Newbie

Ciao Jeremy e grazie per l'attenzione dimostrata.

Scusami se non ho risposto subito, ma ho dovuto lasciare un attimo da parte quel software... e ora lo riprendo.

Ho visto il tuo esempio... e ho visto il metodo DoApplication() che, praticamente, dovrebbe occuparsi di mandare di tanto in tanto messaggi sullo stato dell'applicazione al sistema operativo (del tipo: "non sono morta... sto lavorando al limite delle forze!").
In questo modo tu permetti l'utilizzo della GUI da parte dell'utente durante l'elaborazione... non è precisamente quello che serve a me, ma io potrei sempre disabilitare il form nell'attesa che l'elaborazione finisca, per evitare che l'utente faccia "danno"... e sperando che riesca a creare una finestra non modale nel frattempo (perché quelle modali bloccano l'esecuzione del codice, mentre le non modali mi davano problemi essendo sullo stesso thread).
Il problema però sta nel fatto che il DoApplication rallenta enormemente l'applicazione. Io ho fatto 2 cicli annidati, il primo viene eseguito 8 volte e, al suo interno, il secondo viene eseguito 1250 volte (per ognun passo degli 8 cicli esterni... e arriviamo sempre a 10mila). Al termine di ognuno dei cicli esterni invoco il DoApplication() (in modo da non fare 10mila if()), ma l'applicazione passa da 30 secondi a 3 minuti e 10 circa per solo 8 DoApplication()!!!)....
Nel frattempo l'applicazione risulta attiva, poi bloccata (non risponde), poi nuovamente attiva ecc...

Io mi accontenterei semplicemente che l'applicazione non risultasse morta (ossia che inviasse ogni tanto dei messaggi al sistema operativo dicendo di essere viva)... non mi serve davvero niente altro... né che l'utente ci possa interagire... né altro... solo far sì che se c'è un'elaborazione di 3 minuti il sistema operativo non blocchi l'esecuzione parlando di stallo.

Grazie ancora per l'aiuto.

Chiaramente nel frattempo non mi sto cullando sugli allori sperando che solo tu mi fornisca la soluzione... sto anche cercando un sacco sul web e facendo prove... magari trovo una soluzione e la posto qui... sicuramente si dimostrerà utile per qualcun altro nel futuro... ;)

Pierpaolo

Jeremy Profilo | Guru

Ciao PierPaolo.

>Chiaramente nel frattempo non mi sto cullando sugli allori sperando che solo tu mi fornisca la soluzione... sto anche >cercando un sacco sul web e facendo prove... magari trovo una soluzione e la posto qui... sicuramente si dimostrerà >utile per qualcun altro nel futuro... ;)

A parte il fatto che il metodo si chiama Application.DoEvents e non DoApplication ..... si capisce benissimo che hai fatto delle ricerche ed hai, da solo, capito esattamente il funzionamento di tale metodo .... e tutte le problematiche legate all'uso di esso.
La mia voleva solo essere una soluzione, se vogliamo, un pò superficiale.
La soluzione del MultiThreading, rimane, a parer mio, quella più gettonata .... ma ci sarebbe da lavorarci un pò su.

Ad ogni modo, se mi avanza qualche ora(ma non ti prometto nulla), vedrò di riprodurre lo scenario per fare delle prove e poi ti faccio sapere ...
Ciao

phai Profilo | Newbie

Ciao,

>A parte il fatto che il metodo si chiama Application.DoEvents e non DoApplication .....
Ops... scusa la fretta :D

>si capisce benissimo che hai fatto
>delle ricerche ed hai, da solo, capito esattamente il funzionamento
>di tale metodo .... e tutte le problematiche legate all'uso di
>esso.

Scusa se sono stato frainteso: il mio messaggio era solo per far capire in generale che non sto aspettando la pappina pronta... su altri forum mi è spesso successo di trovare gente che voleva qualcuno che gli risolvesse gli esercizi per la scuola... o gente che pretendeva una soluzione senza applicarsi nemmeno a dare uno sguardo alla documentazione ufficiale dopo che gli si proponeva una soluzione... e questo non sempre è tollerabile, perché uno non deve cullarsi solo perché ha chiesto aiuto in un forum... senza continuare a cercare... tutto qui :)

>La mia voleva solo essere una soluzione, se vogliamo, un pò superficiale.
Ma sì... :) so benissimo che qui stiamo discutendo di metodo di risoluzione al problema...
Poi, non chiederei mai a qualcuno di scrivermi una classe ad hoc che mi risolva il problema... anzi il solo fatto che hai creato un progettino per farmi vedere come funzionava quel metodo per me è stato un gesto importantissimo che denota quanto tu ci tenga a quello che fai qui sul forum...

>La soluzione del MultiThreading, rimane, a parer mio, quella più gettonata .... ma ci sarebbe da lavorarci un pò su.

Ho provato anche a cercare di lavorare col CoWaitForMultipleHandles. A quanto pare si utilizza per tenere in vita le code di Windows (dovrebbe anche essere richiamato dal DoEvents)... o qualcosa del genere...
Chiaramente metterlo in ogni iterazione o eseguire delle if() può essere molto lungo.... così ho provato a farlo eseguire in un thread separato.
Un'implementazione del tipo
1. istanzia nuovo thread e lancialo
2. esegui tutti i tuoi lunghi cicli
3. termina il thread
E all'interno del thread un while(true) che esegue il comando e si arresta per qualche secondo.

Il problema è che lavorando con il multithread il sistema operativo dopo un minuto di loop si blocca lo stesso... in quanto il comando di cui sopra viene lanciato da un thread differente da quello che sembra bloccato. Questo sia che usi il CoWaitForMultipleHandles() che il DoEvents()... :(

Attualmente: facendo le if() supero i 5 minuti con il DoEvents() e i 3 con il CoWaitForMultipleHandles()... e forse è davvero l'unica soluzione... visto che le interfacce grafiche (io lavoro con Windowf Form, ma anche usando WPF non cambia nulla) lavorano in STA...

>Ad ogni modo, se mi avanza qualche ora(ma non ti prometto nulla), vedrò di riprodurre lo scenario per fare delle prove e poi ti faccio sapere ...
Ti ringrazio ancora per la grande disponibilità...
Comunque, se davvero hai del tempo da perdere, dimmelo che snellisco il progettino su cui sto lavorando lasciando solo quello di cui stiamo parlando e te lo faccio avere...

Ancora grazie

Pp

Jeremy Profilo | Guru

Ciao.
Sei tu che hai frainteso .... in nessun modo le mie volevano essere critiche, anzi ho apprezzato lo spirito di ricerca per capire a fondo il metodo DoEvents, ma in generale, per capire nel dettaglio un suggerimento, se vogliamo, un pò superficiale.

Purtroppo sul forum è difficile esprimere il *tono* ma, le mie non volevano essere in alcun modo delle critiche.
Rinnovo comunque l'impegno, qualora trovassi del tempo, di fare delle prove e poi .... ti faccio sapere.

Ciao

Jeremy Profilo | Guru

Ciao.
Prova a dare un occhio al risultato che ottieni con questo progettino.
Per quanto riguarda il tempo di elaborazione, non ci siamo ..... nel senso che i tempi sono inevitabilmente lunghi, ma, quanto meno, l'applicazione non si blocca.
Per mia comodità l'ho scritto in Vb e neanche troppo ordinato/corretto , se il risultato ti interessa, potremmo sistemarlo e tradurlo in C# , cosi che tu possa adattarlo alle tue esigenze.
Facci sapere...
Ciao

Partecipa anche tu! Registrati!
Hai bisogno di aiuto ?
Perchè non ti registri subito?

Dopo esserti registrato potrai chiedere
aiuto sul nostro Forum oppure aiutare gli altri

Consulta le Stanze disponibili.

Registrati ora !
Copyright © dotNetHell.it 2002-2024
Running on Windows Server 2008 R2 Standard, SQL Server 2012 & ASP.NET 3.5