Newsgroup: it.comp.dotnet
Titolo: Come abortire un backgroundworker

Ciao a tutti.
Sviluppando un editor/debugger javascript, mi sono trovato nella 
necessità di lanciare un interprete javascript (Jint) durante l'editing 
del codice. Siccome avevo avuto un'esperienza precedente con i 
BackgroundWorker, ne ho sottoclassato uno per le mie esigenze, 
sfruttando anche il fatto che le eccezioni del thread in background 
(l'interprete) me le ritrovavo comodamente in 
RunWorkerCompletedEventArgs dentro ad Error.
Però provando uno script con un loop infinito mi sono reso conto che 
dovevo interrompere il thread dell'interprete ad ogni nuova pressione di 
tasto, così ho cercato un modo per farlo.
Mamma google mi ha detto senza troppi giri di parole che un 
backgroundworker va interrotto in modo "graceful" (CancelAsync) e non 
con un Abort del thread, a causa del fatto che BackgroundWorker usa una 
ThreadPool, e che comunque usare Abort non è mai una buona scelta.
Però nel mio caso non sono io a controllare l'esecuzione in background, 
con cicli in qualche modo "controllabili" ad ogni passaggio, ma passo la 
palla all'engine javascript che potrebbe avere un meccanismo di stop 
oppure no (non ho controllato se Jint ce l'abbia, ma un giorno potrei 
mettere v8 o spidermonkey, vorrei trattarlo come una blackbox), dunque 
credo che l'abort abbia un senso.
Dunque mi sono buttato su una soluzione "ibrida" con BackgroundWorker + 
Thread classico, scopiazzando un post su Stackoverflow ed aggiungendo il 
rilancio delle eccezioni al BackgroundWorker che le cattura in Error:
var backgroundWorker = new BackgroundWorker(){WorkerSupportsCancellation 
= true};
backgroundWorker.DoWork += (sender, args) =>
	{
		Exception workerException = null;
		var thisWorker = sender as BackgroundWorker;
		var _child = new Thread(() =>
                  	{
            			try
				{
					//..Do Some Code
				}
				catch (Exception ex)
				{
					workerException = ex;
				}
			});
		_child .Start();
		while (_child.IsAlive)
		{
			if (thisWorker.CancellationPending)
			{
				_child.Abort();
				args.Cancel = true;
			}
			Thread.SpinWait(1);
		}
                 if (workerException != null)
                     throw workerException;
	};
[...]
backgroundWorker.RunWorkerAsync(parameter);
  //..Do Something...
backgroundWorker.CancelAsync();
Vedete qualche controindicazione evidente alla cosa?
Sembra funzionare bene, ma con i thread non si sa mai...
Grazie e ciao

 Vedete qualche controindicazione evidente alla cosa?
non fare l'Abort del child thread, wrappa il child thread in una tua 
classe che è quella che fa il lavoro "ricorsivo" e metti in piedi li un 
meccanismo di "graceful exit"
.m
-- 
Mauro Servienti
{C67C0157-5D98-4733-A75E-93CAEE4BADC8}
Microsoft MVP - Visual C# / MCTS
http://mvp.support.microsoft.com
blog @ 
http://milestone.topics.it
whynot [ at ] topics [ dot ] it

 meccanismo di "graceful exit"
capisco che sarebbe la cosa ideale, però il child thread in questione 
non fa un lavoro ricorsivo o iterativo che controllo, in realtà fa una 
cosa come questa:
result = _jEngine.Run(_script);
con _jEngine interprete javascript che non ha un metodo Abort o Stop 
(nella fattispecie Jint, che può gestire un evento Break ma il punto di 
break va impostato prima del Run).
Immagina che io stia scrivendo questo script nell'editor dimenticandomi 
di scrivere "i++" (cosa comunque lecita):
function pippofun()
{
	var result = 0;
	var i = 0;
 
	for(i=0; i<1000;)
	{
		result++;
	}
	return result;
}
var pluto = pippofun()
ovviamente l'interprete va in loop infinito prima che possa aggiungere 
"i++" nel for, perché viene lanciato ad ogni pressione di tasto, ed io 
devo poterlo interrompere e far ripartire alla successiva pressione di 
un tasto.
Ecco in sostanza la questione, sicuramente mi ero spiegato molto male (e 
forse anche adesso, ma spero che tu mi abbia capito lo stesso).
Intanto grazie per la risposta.
Ciao

 Ciao
La sostanza non cambia, l'importante è che dentro il loop ci sia il 
controllo di una variabile booleana che provochi l'uscita dalla 
funzione.
Abortire i thread implica la potenziale perdita di risorse ed è un 
rimedio estremo a cui non bisognerebbe mai ricorrere.
-- 
Raffaele Rialdi  
http://www.iamraf.net
Weblog: 
http://blogs.ugidotnet.org/raffaele
Microsoft MVP profile 
https://mvp.support.microsoft.com/profile/raffaele
UGIdotNET - 
http://www.ugidotnet.org/

 controllo di una variabile booleana che provochi l'uscita dalla funzione.
intervengo in quanto collega del buon sabba (e "visionatore" del codice).
La situazione è questa:
IComponenteEsterno ce = ...;
ce.Run();
il metodo 'ce.Run()' va richiamato internamente a un background worker. 
Tale metodo può anche metterci svariati minuti e il componente esterno 
non ha un altro metodo tipo 'Stop()' per fermare l'esecuzione di 
'Run()'. Quindi niente loop, niente variabili di controllo, niente 
gracefully exit (a meno che anche a me non sfugga qualcosa).  :-)
Ciao e grazie,
Giulio
-- 

 a me non sfugga qualcosa).  :-)
credo di aver capito che l'unica è l'Abort.


 Giulio
Prego


 Beh è il componente esterno ad essere fatto male ... ;)
Giusto per chiarire, abortire il thread di quel componente può dare 
grossi problemi.
Mettiamo ad esempio che faccia delle aperture di file o di oggetti 
grafici o comunque qualsiasi altra cosa in cui viene aperto un handle.
Alla chiusura forzata del thread quelle risorse rimarranno "leaked" e a 
lungo andare il processo dovrà necessariamente essere chiuso per 
poterle chiudere e recuperarle.


 chiudere e recuperarle.
Al momento prevedo che il tipo di script che verranno fatti con l'editor 
javascript (che è a sua volta contenuto in un ide) non dovrebbero 
impegnare risorse persistenti, ma in seguito la cosa potrebbe cambiare e 
comunque giustamente ci possono essere leaks, dunque come dite tu e 
Mauro la cosa sarebbe da evitare, per ora vado avanti così ma nel 
frattempo richiedo un "ticket" agli sviluppatori di Jint per uno stop 
graceful.
Grazie ancora a tutti.

 graceful.
Quoto.
Mauro e Raf hanno *giustamente* messo in evidenza che l'abort di un 
thread non è una pratica da incoraggiare. A questo punto però, mi 
chiedo... se il thread che viene abortito è caricato in un AppDomain 
separato e che viene scaricato *in toto*, gli handle che sono leaking 
vengono rilasciati? (ok, mi sa che sto facendo fanta-design, vero?).
Ciao,
Giulio
-- 

 sa che sto facendo fanta-design, vero?).
Ovviamente no, non vengono rilasciati in quanto gli handle di cui 
parlavo sono di tipo nativo mentre gli appdomain sono un'astrazione del 
framework.
Quando un processo viene chiuso, Windows provvede a rilasciare tutti i 
kernel object (si, a livello kernel esiste il concetto di oggetto) che 
sono associati al processo.
Per essere più precisi, il counter dell'oggetto viene decrementato e se 
arriva a zero il kernel object viene disallocato/rilasciato.
I kernel object possono infatti essere condivisi tra più processi (per 
esempio named mutex, file, registry, etc. etc.)


 leaking vengono rilasciati? (ok, mi sa che sto facendo fanta-design, vero?).
no, "likki" comunque :-)


 sono associati al processo.
[cut]
Si, questo lo sapevo. Comunque la spiegazione dettagliata che hai dato 
non lascia ombra di dubbi.
Grazie,
Giulio
-- 

 Giulio
Prego :)
