Progetto: Creare un CRONTAB multithread in .net

lunedì 29 giugno 2009 - 20.54

Lanello Profilo | Senior Member

Ciao a tutti,

sto cercando di creare una sorta di crontab (http://it.wikipedia.org/wiki/Cron per chi non sa di che parlo ) in .net

ho creato in un db a mio piacimento una tabella del tipo:

id_lavoro, nome_funzione, argomenti, m, h, dom, mon, dow

dove "nome_funzione" indica il nome di una funzione disponibile in un mio webservice
dove "argomenti" indica una lista di argomenti da passare alla funzione da chiamare, in elenco semplice suddiviso da virgole
dove "m" indica il minuto del giorno in cui eseguire la funzione
dove "h" indica l'ora del giorno in cui eseguire la funzione
dove "dom" indica il giorno del mese (Day Of Month)
dove "mon" indica il mese dell'anno
dove "dow" indica il giorno della settimana (Day Of Week) (0= domenica, 6=Sabato)

gli argomenti che riguardano il momento dell'esecuzione della funzione hanno come wildcard il carattere '*'

ho impostato un servizio windows (che visto che ho tanta fantasia ho chiamato Crontab ) dove ad intervalli di 30 secondi vado ad eseguire la query riportata di seguito

(premetto che preferisco vb tra i linguaggi .net )
Il codice sorgente non è stato renderizzato qui
perchè non c'è sufficiente spazio.
Clicca qui per visualizzarlo in una nuova finestra

e ricevo come recordset risultante l'elenco delle funzioni e variabili da eseguire in quel momento

per l'esecuzione di una funzione chiamata dinamicamente utilizzo il semplice comando

CallByName

come ho detto prima utilizzo il crontab per chiamare dinamicamente delle funzioni di un webservice passando semplicemente il nome della funzione.

istanzio il webservice in una variabile locale tipo "ws"

e dopo semplicemente mi limito ad effettuare le chiamate ai lavori del crontab nel modo seguente

Il codice sorgente non è stato renderizzato qui
perchè non c'è sufficiente spazio.
Clicca qui per visualizzarlo in una nuova finestra

e tutto funziona correttamente.

ora chiedo a voi Guru del multithreading di aiutarmi a trasformare il tutto in un sistema multiprocesso per terminare al 100% questo sistema.



----------------------------------------------------------------------
http://www.flashinlabs.biz o .com o .net o .it fate voi :P

Jeremy Profilo | Guru

Ciao Marco.
A parte il fatto che, a parer mio, ci sarebbe da lavorare anche sulla sintassi della query e sull' utilizzo del metodo CallByName piuttosto che di un'interfaccia......nello specifico .... cosa ti serve sapere?
Hai già un'idea di quali sono le procedure che vuoi portare su thread diversi?
Queste, interagiscono in qualche modo con il Thread Principale?


Facci sapere...
Ciao

Lanello Profilo | Senior Member

>Ciao Marco.

Ciao,

>A parte il fatto che, a parer mio, ci sarebbe da lavorare anche
>sulla sintassi della query e sull' utilizzo del metodo CallByName
>piuttosto che di un'interfaccia......

Benissimo, ogni consiglio per migliorare questo strumento per me sarà utilissimo, io non essendo espertissimo ho proposto quello che sono riuscito a realizzare fino ad oggi proprio per avere commenti, suggerimenti e correzioni per migliorare.

>nello specifico .... cosa ti serve sapere?

lo strumento funziona, ma i metodi del webservice che vengono chiamati dinamicamente dalla routine "crontab" se non erro girano sul thread principale, vorrei avere qualche dritta sul come poter fare a gestire N thread separati in quanto la funzione principale di questo crontab sarà quella di eseguire statistiche complesse e non troppo veloci, che gireranno di notte ad intervalli prestabiliti per generare dei report e riversarli in una cartella del server ad uso e consumo di chi ne ha bisogno in azienda.

>Hai già un'idea di quali sono le procedure che vuoi portare su thread diversi?

No perchè appunto è tutto gestito da database, in ogni momento il cliente può togliere o aggiungere una procedura dal crontab

>Queste, interagiscono in qualche modo con il Thread Principale?

Assolutamente no, sono principalmente come ho descritto sopra elaborazioni notturne di report troppo complessi per essere generati "al volo"


Grazie della disponibilità.
----------------------------------------------------------------------
http://www.flashinlabs.biz o .com o .net o .it fate voi :P

Jeremy Profilo | Guru

Ciao Marco.
Facciamo una cosa .... comincio a darti qualche informazione su come creare un thread separato e come avviarlo ..... almeno partiamo con qualcosa di concreto ... perchè mi sembra che per il resto tu abbia già le idee piuttosto chiare.

Pertanto ....
Private NewThread as Threading.Thread Private Sub InizioDellaProcedura NewThread = new Threading.Thread(AddressOf Procedura1) NewThread.Start End Sub Private sub Procedura1 Ws.WebMethod1 End Sub

Ovviamente questo è solo un esempio base .... ma non riuscendo bene a capire il tuo livello di conoscenza .... mi trovo un pò difficoltà a darti informazioni mirate nello specifico delle tue esigenze ...
Ad ogni modo ... rimango a disposizione per ulteriori dettagli.

Facci sapere...
Ciao

Lanello Profilo | Senior Member

vediamo se riesco a farti capire...

ho creato questo strumento perchè ci sono delle statistiche su base giornaliera, MOLTO complesse che devo eseguire nottetempo, e che vanno a pescare dati da più server di database nelle varie sedi dell'azienda, quindi siamo di fronte a latenze di connessione, tempi di risposta dei diversi server, e mille altri problemi...

lo strumento che ho realizzato funziona benissimo anche se uso un sistema semplicissimo, da una piccola interfaccia ad una tabella l'utente che gestisce la generazione di queste statistiche può in ogni momento variare dei parametri, che vanno dalla funzione (fra quelle disponibili nel webservice installato sul server stesso che esegue il crontab), i vari parametri da passare alla funzione stessa, e tutte le variabili che ho descritto ad inizio thread per la gestione dell'esecuzione "a tempo".

il problema di fondo è che mi si "incolonnano" i processi ed il server pur avendo un carico di rete e di processore minimo, a causa delle latenze sopra descritte non finisce di elaborare tutte le statistiche per l'orario di apertura degli uffici.

quindi, facendo nel processo principale una query alla tabella dei lavori da fare, vado a lanciare una chiamata al callbyname del tipo:

Il codice sorgente non è stato renderizzato qui
perchè non c'è sufficiente spazio.
Clicca qui per visualizzarlo in una nuova finestra

lavoro è il nome del datatable che contiene ovviamente l'elenco dei processi da eseguire.

è ogni singola chiamata al "CallByName" che vorrei portare in un diverso thread con ovviamente tutti gli argomenti di cui ha bisogno per eseguire il lavoro specificato nel db, per riuscire ad eseguire il calcolo di queste statistiche in contemporanea usando il più possibile la banda disponibile e la potenza del server per riuscire a generare le suddette statistiche in tempo per l'apertura mattutina degli uffici.

spero proprio di essere riuscito a chiarire la situazione.
----------------------------------------------------------------------
http://www.flashinlabs.biz o .com o .net o .it fate voi :P

Jeremy Profilo | Guru

>vediamo se riesco a farti capire...
Vediamo se ho capito...
Tutte le procedure che attualmente vengono eseguite nel thread principale, pertanto una di seguito all'altra ..... in coda per intenderci .... vorresti invece venissero eseguite contemporaneamente una indipendente dall'altra.

Se quello che ho capito è corretto .... non vedo nessun tipo di problema nel fare ciò che chiedi, in quanto è possibile leggere tutti metodi con i relativi argomenti dalla DataTable e far partire più thread contemporaneamente che invocano i WebMethod di più istanze del WebService(ovviamente).

Una cosa tipo:
Private sub StartLavoro for each row as datarow in Lavoro.rows dim NewThread as new threading.thread(address of Procedura) NewThread.Start(Row) next End Sub Private sub Procedura(byval Row as DataRow) dim ws as New TuoServizio CallByName(ws, Row("nome_funzione"), CallType.Method, Split(Row("argomenti"), ",")) End Sub

Non so se funziona perchè non l'ho provata .... e per giunta l'ho scritta al volo senza Debug .... ad ogni modo, facci sapere....
Ciao

Lanello Profilo | Senior Member

>>vediamo se riesco a farti capire...
>Vediamo se ho capito...

hai capito benissimo!!!

>non vedo nessun tipo di problema nel fare ciò che chiedi

ehhh invece il problema che non riesco ad oltrepassare è la chiamata dal thread.start ad una funzione che richiede il passaggio dei parametri, infatti...

>dim NewThread as new threading.thread(address of Procedura)
...
>Non so se funziona perchè non l'ho provata .... e per giunta
>l'ho scritta al volo senza Debug ....

non funziona perchè il compilatore dice che:

>Risoluzione dell'overload non riuscita perché nessun "New" accessibile può essere chiamato con questi argomenti:
> 'Public Sub New(start As System.Threading.ParameterizedThreadStart)': Il metodo "Private Sub AvviaLavoro(lavoro As System.Data.DataRow)" >non ha la stessa firma del delegato "Delegate Sub ParameterizedThreadStart(obj As Object)".

ma proprio adesso mentre incollavo l'errore ho visto che semplicemente il problema è che il threadstart accetta un parametro generico object mentre io ho specificato datarow, ho provveduto a cambiare il parametro accettato dalla funzione come object ed a creare una variabile locale all'interno della funzione chiamata, ripassando poi il contenuto dell'oggetto alla variabile locale creata come datarow come descritto qui sotto.


Private Sub AvviaLavoro(ByVal oggetto As Object)
Dim lavoro As DataRow
lavoro = oggetto
...
End Sub

con questo, ti ringrazio infinitamente e ti invito se hai tempo a descrivermi le modifiche che mi consigliavi all'inizio sulla query e sull'uso del callbyname...

nel frattempo accetto la risposta in quanto esaustiva e corretta.

grazie.
----------------------------------------------------------------------
http://www.flashinlabs.biz o .com o .net o .it fate voi :P

Jeremy Profilo | Guru

Ciao Marco.
Bene.
> ti invito se hai tempo
>a descrivermi le modifiche che mi consigliavi all'inizio sulla
>query e sull'uso del callbyname...

Ok .... ma cominciamo subito da un'altro problema, e cioè .... è buona abitudine impostare OptionStrict On per impedire conversioni implicite e motivo di errore a run-time.
in questo caso però, non avresti potuto scrivere questo codice:
Private Sub AvviaLavoro(ByVal oggetto As Object) Dim lavoro As DataRow lavoro = oggetto ... End Sub

in quanto OptionStrict non ti permette di assegnare il riferimento ad un oggetto di un tipo ad un'altro riferimento ad oggetto di un'altro tipo senza conversione esplicita ...pertanto, avresto dovuto poi risolvere in questo modo:
Private Sub AvviaLavoro(ByVal oggetto As Object) Dim lavoro As DataRow=directcast(oggetto,DataRow) ... End Sub

Per il resto bisognerebbe affrontare il discorso delle query parametriche (che potremmo approfondire in seguito) e quindi la tua query diventerebbe:
Il codice sorgente non è stato renderizzato qui
perchè non c'è sufficiente spazio.
Clicca qui per visualizzarlo in una nuova finestra
tenendo comunque presente che .... ad ogni modo ... è sbagliata, probabilmente, la struttura della tabella del db a quale fai riferimento, in quanto non tipizzata a dovere.
Se stai lavorando con delle date, dovresti tipizzare i campi come tali (DateTime) e scrivere la tua query con una sintassi adeguata ai tipi che devi valutare.
Mi sembra di capire (si capisce dalla sintassi della query) che i campi della tabella siano tipizzati tutti come valori stringa .... e questo, seppur *funzionante*, è quanto meno *brutto*.

Riguardo invece al CallByName, troverei un'alternativa (e ce ne sono) in quanto è un metodo che fa parte dell'assembly Microsoft.VisualBasic, il quale, è stato portato dietro fino alla versione 3.5 del Framework per motivi di compatibilità con applicazione compilate per versioni precedenti .... ma non è detto che questo assembly verrà mantenuto nella prossima versione del framework, pertanto ti troveresti di fronte ad una incompatibilità della tua applicazione per versioni successive del Framework.
E' bene, anche in questo caso, disattivare l'Import automatico di tale Namespace dalle proprietà di progetto (scheda Riferimenti).
L'alternativa a CallByName sarebbe quella di creare delle interfacce comuni a varie tipologie di oggetti e quindi fare riferimento ad esse per avere la tipizzazione necessaria a chiamare metodi funzioni e proprietà senza ricorrere a CallByName.

Anche questo , ad ogni modo, è possibile approfondirlo con calma.

Ciao

Lanello Profilo | Senior Member

Grazie di nuovo jeremy

>Dim lavoro As DataRow=directcast(oggetto,DataRow)

modifica effettuata immediatamente (il bello è che lo sapevo anche, ma nell'euforia della trasformazione da singol-thread a multi-thread chi c'ha pensato )


>Per il resto bisognerebbe affrontare il discorso delle query
...
>è sbagliata,probabilmente, la struttura della tabella del db a quale fai
>riferimento, in quanto non tipizzata a dovere.
>Se stai lavorando con delle date, dovresti tipizzare i campi
>come tali (DateTime) e scrivere la tua query con una sintassi
>adeguata ai tipi che devi valutare.

so bene che quando si lavora con le date si deve tipizzare i campi nel modo giusto, ma se pensi bene alla struttura della tabella che devo realizzare risulta un po difficile usare campi diversi dal varchar

devo memorizzare il minuto in cui viene eseguito un comando il tipo migliore sarebbe l'integer unsigned (valore min 0 valore max 59) però c'è
anche la possibilità di eseguire un comando in "ogni" minuto contrassegnato dal carattere jolly "*"

ecc ecc per tutte le variabili che sono

> m (minuto) (0-59)
> h (ora) (0-23)
>dom (day of month, il giorno del mese) (1-31)
>mon (month, il mese dell'anno) (1-12)
>dow (day of week, il giorno della settimana) (0-6 dove 0=domenica, 6=sabato)

e per ognuno c'è la possibilità di impostare "*"

quindi se per esempio voglio effettuare un backup del db alle 23.20 tutti i giovedì sera nel db io imposterò

m=20
h=23
dom=*
mon=*
dow=5

per questo ho usato il varchar(2)


>Riguardo invece al CallByName, troverei un'alternativa (e ce
>ne sono) in quanto è un metodo che fa parte dell'assembly Microsoft.VisualBasic,
>il quale, è stato portato dietro fino alla versione 3.5 del Framework
>per motivi di compatibilità con applicazione compilate per versioni
>precedenti .... ma non è detto che questo assembly verrà mantenuto
>nella prossima versione del framework, pertanto ti troveresti
>di fronte ad una incompatibilità della tua applicazione per versioni
>successive del Framework.

prima di trovare aiuto da te mi ero imbattuto nei delegates per le chiamate dinamiche alle funzioni... secondo te può essere una via da seguire?

Grazie di nuovo
----------------------------------------------------------------------
http://www.flashinlabs.biz o .com o .net o .it fate voi :P

Jeremy Profilo | Guru

Ciao Marco.
>so bene che quando si lavora con le date si deve tipizzare i campi nel modo giusto, ma se pensi bene alla struttura della tabella che devo realizzare risulta un po difficile usare >campi diversi dal varchar
>
>devo memorizzare il minuto in cui viene eseguito un comando il tipo migliore sarebbe l'integer unsigned (valore min 0 valore max 59) però c'è
>anche la possibilità di eseguire un comando in "ogni" minuto contrassegnato dal carattere jolly "*"
>
>ecc ecc per tutte le variabili che sono
uuummm .... io avrei comunque utilizzato un campo DateTime, poi il framework, ti offre un mare di possibilità per estrarre solo la parte del valore che ti serve (come anche T-SQL)
Per quanto riguarda l'asterisco, ci potrebbero essere altri modi per selezionare i record a prescindere dal valore dei minuti.
Ad ogni modo ... non è che non vada bene come hai fatto tu .... è solo un pochino meno bello.

>mi ero imbattuto nei delegates per le chiamate dinamiche alle funzioni... secondo te può essere una via da seguire?
uuummmm .... per lo scopo esclusivo di sostituire CallByName, credo proprio che non sia il caso .... l'uso dei delegates è sicuramente previsto in applicazioni MultiThreading .... ma, in questo contesto, non credo sia il caso (di solito si usano per gestire operazioni cross Thread).
Per quanto riguarda l'uso delle interfacce, potrebbe essere un problema, applicarne l'uso nello scenario della tua applicazione ... mi orienterei forse (dico forse) all'uso delle Reflection.
Insomma ... qualunque cosa tranne che CallByName.

Ciao


Lanello Profilo | Senior Member


>Insomma ... qualunque cosa tranne che CallByName.


HAHAHAHAHAHAHA!!!!

Mitico!!!!

ok mi metto a studiare ;)


grazie
----------------------------------------------------------------------
http://www.flashinlabs.biz o .com o .net o .it fate voi :P
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