Garantire l'esecuzione completa delle attività prima di stoppare il Servizio Windows

di il
4 risposte

Garantire l'esecuzione completa delle attività prima di stoppare il Servizio Windows

Gentili,
sinceramente avevo postato una richiesta simile in altro sito per non rompere le scatole sempre ai frequentatori di IProgrammatori, ma senza ottenere risposta; pertanto se sto violando la regola del cross posting provvederò immediatamente a rimuoverlo.
Ho creato un servizio di Windows usando .Net Core 3.1 e l'oggetto BackgroundService ospitato tramite IHostBuilder.
Dovendo effettuare periodicamente la lettura/scrittura/cancellazione da due db diversi posizionati su due macchine separate, prevede anche tutta una serie di verifiche di disponibilità dei db e meccanismi per evitare eccezioni, etc; il mio problema riguarda il caso in cui per motivi di manutenzione o di altro tipo si debba procedere allo stop del servizio garantendo l'esecuzione dell'intera lista di operazioni, essendo collegate: es. lettura nel primo db, scrittura nel secondo, cancellazione dal primo db.
Dovendo ancora valutare la fattibilità delle transazioni distribuite (leggendo da varie fonti mi sembra che comunque Entity Framework non le gestisca) pensavo di gestire intanto la chiusura del servizio nel metodo StopAsync() con una variabile che indica la situazione del "job".
Allego il link al codice , dove ho semplificato la reale procedura invece di inserire tutto il codice; ho provato la cosa e mi sembra funzionale, ma mi servirebbe una conferma dai più esperti di me.
Grazie e buon weekend.
Lucius

4 Risposte

  • Re: Garantire l'esecuzione completa delle attività prima di stoppare il Servizio Windows

    Forse non ho capito la domanda, ma quando il servizio viene arrestato dall'SCM di Windows, viene inviata al servizio una richiesta di concludersi, quindi il programma può predisporsi alla chiusura terminando regolarmente come qualsiasi altra applicazione nel momento in cui intercetta questa richiesta.

    In breve, che io sappia, non c'è un arresto forzato del servizio tipo quello effettuato dal Task Manager sui processi per quali si esegue il comando "Termina attività" o "Termina processo", che invece sono coercitivi.

    Ciao!
  • Re: Garantire l'esecuzione completa delle attività prima di stoppare il Servizio Windows

    Salve Alka,
    come sempre ti ringrazio per la risposta, in effetti non mi sono espresso correttamente.
    Quando un'app viene eseguita come servizio e ne viene richiesto l'arresto dal S.O. oppure dall'utente nella scheda servizi > Arresta servizio, lo stesso dovrebbe arrestarsi gradualmente fino al completamento del task, non come avviene invece killando un processo da Task Manager.
    Solo che facendo delle prove con il listato incluso nel link, ho notato che il metodo ExecuteAsync() non viene portato a termine ma stoppato nel mezzo dell'esecuzione; un po' come se fosse killato brutalmente da Task Manager per intendersi.
    Aggiungendo invece il metodo StopAsync() con il while sulla variabile occupato in effetti il metodo viene concluso correttamente, ma mi serviva una conferma per sicurezza.

    PS: nel mio caso essendo il problema essenzialmente relativo a operazioni di lettura/scrittura/cancellazione da effettuarsi su più db avrei potuto rendere la faccenda affidabile al 99% ricorrendo alle Transazioni Distribuite; Entity Framework Core le supporta correttamente, ma solo pocccogiuda se usato in App di tipo .Net framework e non .Net Core.
    Quando si dice la sfiga.

    Ancora saluti.
    Lucius
  • Re: Garantire l'esecuzione completa delle attività prima di stoppare il Servizio Windows

    Ciao Lucius

    Nella documentazione Microsoft spiegano bene il funzionamento del background worker ( https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-5.0&tabs=visual-studio )

    In pratica:
    - StartAsync viene richiamato con un CancellationToken che il sistema imposta come "cancellation request" quando il servizio deve fermarsi.
    - Se non viene gestito correttamente (fermato in breve tempo), viene detto che il servizio viene killato brutalmente

    Probabilmente ti server implementare il metodo StopAsync perchè il tempo necessario per terminare le tue operazioni supera il limite che Windows considera valido prima di killarlo brutalmente.

    A questo scopo puoi valutare tu se sia più corretto implementare lo StopAsync (più o meno come hai fatto) o se abortire le modifiche che stai facendo (rollback della transazione). Ovviamente se il tuo obbiettivo è "terminare le operazioni in corso" allora va bene implementare StopAsync, però stai in occhio: se ci mette troppo, comunque chi spegne il PC può dire di terminare brutalmente il processo (a volte viene il messaggio "il processo X ci sta mettendo troppo. Forzare interruzione?"



    Detto questo, faccio due commenti sul codice che hai postato. Prendili come suggerimenti qualora quello fosse il tuo codice vero, se è solo un esempio buttato lì, allora ignora pure:
    - Nella ExecuteAsync usi il metodo "Sleep" della classe Thread. Di solito nel paradigma async/await è più adatto un "await Task.Delay(x)"
    - Nella StopAsync fai un ciclo while (occupato). Di suo tutto bene, però sarebbe bene mettere al suo interno un Task.Delay, altrimenti il processore continua a ciclare su quella if usando delle risorse inutili. Anche un Delay di un centesimo di secondo può fare la differenza
    - Spesso viene usata una variabile boolean come la tua "occupato". A me personalmente piace di più l'approccio del SemaphoreSlim, così si evita proprio l'uso del ciclo "while" poichè gestito tutto dal semaforo.


    Vedi te.
    Ciao e buona giornata
  • Re: Garantire l'esecuzione completa delle attività prima di stoppare il Servizio Windows

    Ciao PiGi78,
    direi suggerimenti ineccepibili, approfonditi e puntuali, meglio di così forse solo una birra ghiacciata sotto l'ombrellone, e dico forse.
    1) Devo portare a termine per forza le operazioni perché non posso gestire transazioni distribuite con relativo rollback dovendo operare su due database posti su due server diversi: essendo un progetto .Net Core, EF Core non è in grado di gestirle. Le ho provate su un progetto .Net framework classico e sarebbe veramente la soluzione migliore solo che in .Net Core non vanno e non mi va di cambiare tutto il progetto;
    2) Sto prevendendo in effetti per scrupolo lo scenario peggiore: siccome le operazioni vengono ripetute circa ogni 5-10 minuti i record da movimentare ad ogni passata sono pochissimi ma vorrei essere sicuro che non si pianti nel bel mezzo di qualche operazione;
    3) Thread.Sleep(x): assolutamente d'accordo è che ogni volta mi scappa di usarlo, cambio subito;
    4) Task.Delay nel while: ri-ri-ri-ri-assolutamente d'accordo.

    PS: ho fatto un confronto con una prova di carico così al volo con un While true vuoto e uno contenente un await Task.Delay(500) e devo dire che il carico sul processore non è indifferente: nel primo caso arriva fino al 13% mentre nel secondo max 1%.

    In definitiva, grazie ancora.
    Lucius
Devi accedere o registrarti per scrivere nel forum
4 risposte