Newsgroup: it.comp.lang.delphi
Titolo: Oggetti in thread separati

Salve ragazzi,
nuovo giorno nuova domanda :)
avrei la necessitï¿œ di usare i thread, dopo alcune prove non riesco a 
capire come ragionano.
cosa voglio fare?
la cosa che piï¿œ mi interessa ï¿œ creare un thread per ogni form in modo da 
gestire non solo le varie funzioni della form senza inibire l'uso 
dell'applicazione ma anche di fare ricerche multiple nel database, a 
questo punto quindi ï¿œ evidente che vorrei anche gestire le queries di 
una form all'interno del thread specifico. Come posso fare senza 
impazzire? ;)
grazie :)

 impazzire? ;)
qui trovi un pï¿œ di codice utile a semplificare la (molto complessa) materia 
dei thread.
http://www.midnightbeach.com/jon/pubs/MsgWaits/MsgWaits.html
Per il resto, prova e studia, e continua a provare e a studiare. Non c'ï¿œ la 
bacchetta magica in informatica.
Ale 

 dell'applicazione ma anche di fare ricerche multiple nel database
Le due cose che vuoi ottenere vengono trattate ampiamente in questa 
pagina:
http://delphi.about.com/od/kbthread/Threading_in_Delphi.htm
Ciao
-- 
Morde


grazie ad entrambi la poca dimestichezza con l'inglese limita la 
comprensione delle pagine, farï¿œ del mio meglio. :)

 una form all'interno del thread specifico.
Un form non dovrebbe avere query ma USARE le query esposte da un data
module..
Un form e' interfaccia utente e non logica business.
Il tutto ovviamente se non sei in un contesto MDA, xche sei sei in MDA
cambia tutto.
L'uso di oggetti di accesso ai dati in contesti multithreading impone
delle regole da *rispettare* che potrebbero anche dipendere dalla
tecnologia di accesso ai dati in uso.
Ma le mie domande sono:
1) prima di pensare a cio' che vuoi TU ti sei chiesto se queste cose
collimano effettivamente con le esigenze degli utenti che usano o
useranno la tua applicazione? Magari agli utenti non frega niente di
"parallelizzare" alcune operazioni...
2) sei consapevole della complessita' che ti troversti ad affrontare?
Oppure ti stai lanciando seguendo le indicazioni "a voce" del profeta
di turno che ovviamente non sara' con te a impazzire alle 2 del
mattino xche le cose non vanno e un cliente minaccia di farti causa?
A.

 A.
non avendo lavorato mai, prima d'ora, con il multithreading � ovvio che 
l'idea di complessit� pu� essere parziale o errata, ma in un certo qual 
modo � ovvio che comprendo il duro lavoro a cui sar� sottoposto. Il 
punto � che in alcuni casi potrebbe essere necessario gestire queste 
potenzialit�.
per il resto ti ringrazio, diverse vedute della stessa tematica spesso 
portano consiglio :)

 è questo che incide sulla scelta.
Statistiche lanciate quanto spesso? da dove? da chi? su che mole di
dati?
Con quale frequenza i dati vengono aggiornati al punto da impattare
sui dati delle statistiche?
Non e' che magari riprogettando il sistema di calcolo delle
statistiche si riescono ad ottimizzare i tempi senza dover mettere in
campo i thread?
A volte basta anche un solo indice su una tabella per migliorare le
performance anche del 60%......
A.

 A.
le statistiche (essendo in rete l'applicazione) vengono consultate di 
frequente ed alcune, molto complesse, hanno anche diverse subqueries 
all'interno da qui � nata l'idea dei thread, mentre il primo si occupa 
di gestire la query con tutto quel che ne segue l'utente (per non dire 
il cliente) pu� continuare ad usare l'applicazioni senza interferenze.
ancora grazie alberto

 cliente) puï¿œ continuare ad usare l'applicazioni senza interferenze.
E' facile cadere nella tentazione di associare l'idea del multi thread 
alla velocitï¿œ di recupero dei dati, ma *non ï¿œ cosï¿œ*.
Se vuoi lanciare delle query complesse, non server averle giï¿œ pronte 
per quando l'utente chiamerï¿œ il form deputato a mostrarle, per il 
semplice motivo che tu non sai quando l'utente chiamerï¿œ il form.
Per non avere un dataset obsoleto, sei costretto a fare la query al 
momento in cui viene richiesta.
Non cedere alla tentazione di schedularla in parallelo per essere 
eseguita ogni n secondi:non si fa, non si carica un database di query 
inutili...
E' lecito usare un thread che parallelizzi le letture se sono dati che 
cambiano di poco la popolazione, sto immaginando delle statistiche 
complesse su uno storico, ad esempio. Ma, anche se fosse, a questo 
punto ti suggerisco di utilizzare tabelle dove salvare i risultati 
delle tue query in modo da evitare di rifarle per nulla ogni avvio 
dell'applicazione.
Spendi piï¿œ tempo sull'analisi del tuo problema, e se vuoi scrivi qui 
sopra quello ceh ti viene in mente in modo che possiamo aiutarti a fare 
un'analisi calibrata il piï¿œ possibile sulle esigenze che emergeranno.
Ciao
-- 
Morde


*premessa*
l'applicazione in oggetto era nata per una piccola realtï¿œ, trovatasi poi 
ad immagazzinare innumerevoli dati con almeno 3 postazioni sempre attive 
che generano, caricano etc. documenti di ogni sorta.
Detto questo ï¿œ ovvio che anche una banale ricerca in un ampio range di 
date genererebbe rallentamenti infausti, c'ï¿œ anche da dire che noto una 
spossante lentezza nelle esecuzioni di queries con subqueries (parliamo 
di select con insiemi e sottoinsiemi, operatori IN, NOT IN etc.) da 
queste realtï¿œ ï¿œ nata l'idea dei thread dove delegare l'uso della query 
al thread non avrebbe almeno bloccato l'applicazione e l'utente avrebbe 
anche potuto fare altro.

 potuto fare altro
Come ti ho scritto sopra, questo puï¿œ essere fatto purchï¿œ i dati che la 
query porta a galla sono statici e non ti interessa essere aggiornato 
in tempo reale.
Comunque, dalla descrizione che mi fai dell'architettura, leggo che 3 
client che lavorano costantemente generano rallentamenti sulle query.. 
mi viene spontaneo chiederti: che DB state usando??
Se le query sono sempre le stesse, un db che si definisca tale se le 
mette in chache e le performances vengono azzerate..
Vediamo un pï¿œ questo db chi ï¿œ..
Ciao
-- 
Morde


prima di tutto grazie siete sempre disponibili :)
venendo al punto di domanda il db ï¿œ firebird in release 2.1 ed 
ovviamente configurato come super server.

 configurato come super server.
Hai provato a cercare dei documenti sull'ottimizzazione?
Le query che rallentano cosa fanno? Puoi postarne qualcuna?
Stai leggendo delle viste, ovviamente, invece che le tabelle, vero?
Le tabelle sono tutte in forma normale?
Gli indici sono tutti performanti o utilizzi indici su mostruosi campi 
varchar da 50 caratteri? :-)
Ciao
-- 
Morde


al momento non ho pensato ai documenti sul database.
leggo delle tabelle le viste non so manco come crearle nï¿œ come usarle.
le tabelle sono normali.
gli indici sono tutti integer, con trigger etc. almeno questo :D
ancora grazie. :)

Qua stiamo facendo un puzzle...
Vediamo una buona volta di raccogliere tutte le informazioni
rilevanti.
1) hai n client: quanti?
2) la mole di dati quale e'? la tabella piu' grossa quanti record
contiene?
3) non e'  che il server database gira su un 286 del 1990?? Che hw ha
il server?
4) non è che sullo server girano pure "giocattoli" come exchange o
magari altri motori di database?
La cosa che sfugge a tanti e' che il sw non funziona "a se" ma si
appoggia ad una INFRASTRUTTURA che include la rete, gli switch che la
tengono su, l'hw di ogni singolo pc e il server... Ad esempio la rete
come e' messa?
5) hai campi blob sulle tabelle? Se si come li hai dimensionati?
A.


nessun campo blob, non ci sono immagini ed anche se ci fossero eviterei 
i blob ed userei collegamenti ad i file.
sempre grazie.

 una oltre 20.000 piï¿œ o meno con crescita irrisoria (articoli)
Mi sembrano popolazioni normali. E' chiaro che se usi una tquery con 
select * ci mette un pï¿œ a trasportare 16000 record dal server al 
client.
Una tecnica per mostrare dati di sola lettura ï¿œ quella di appoggiarsi a 
viste, ma l'accesso ai dati deve essere gestito dallo strato apposito 
(il model, MVC). Se non stai usando una modellazione ad oggetti (BOM), 
allora stai usando la gui e i controlli event driven di delphi, quindi 
presumo che tu faccia delle queries con la possibilitï¿œ di fare 
postback, quindi queries dirette sulle tabelle con PK incluse. E questo 
genera un flusso enorme di records attraverso la rete, dal server ad 
ogni client.
Che oggetti stai usando? Tquery? Oppure TClientDataset?
Usi la paginazione?
Posta le info e le queries e vediamo di ottimizzare.
ciao
-- 
Morde


solitamente le queries richiamano solo i campi necessari, cerco di 
evitare gli *.
come giï¿œ detto in precedenza l'uso di viste non l'ho mai fatto quindi 
non saprei manco da dove partire.
uso i componenti IB di delphi con queries updateSQL (clientdataset su 
alcune queries)
la paginazione non l'ho prevista perchï¿œ molte delle queries in oggetto 
sono per statistiche(quindi sum etc) con subqueries.
query che da come risultato gli articoli inevasi degli ordini di un cliente:
select
articoli.Descrizione,
doc_dett.Qta,
doc_dett.Prezzo,
doc_dett.Iva,
doc_dett.FK_articolo,
doc_dett.fk_documento,
doc_dett.pk_doc_dett,
doc_dett.Evasi,
doc_dett.elaborato,
from
DOC_DETT, articoli
where
doc_dett.fk_articolo = articoli.pk_articolo
and
articoli.pk_articolo in
  (select fk_articolo from doc_dett where fk_cliente = :iCliente)
and
doc_dett.fk_documento in (select pk_documento from documenti
where fk_tipodoc in (select pk_TIPODOC from tipi_doc where tipo = 
'ORDINE' and
RIFERIMENTO = 'CLIENTI')
)
and elaborato <> 'S'

C_thelegend scriveva il 04/05/2010 :
Innanzitutto: controlla gli indici come sono stati definiti.
Un indice deve essere di tipo numerico o data, e se di tipo stringa, 
deve essere molto corto! Ad esempio, fk_cliente,cos'ï¿œ ? Un codice 
stringa di 30 bytes? Sarebbe troppo.
WHERE  doc_dett.fk_articolo = articoli.pk_articolo
AND    articoli.pk_articolo IN(SELECT fk_articolo
                               FROM   doc_dett
                               WHERE  fk_cliente = :icliente)
AND    doc_dett.fk_documento IN(
          SELECT pk_documento
          FROM   documenti
          WHERE  fk_tipodoc IN(
                             SELECT pk_tipodoc
                             FROM   tipi_doc
                             WHERE  tipo = 'ORDINE'
                             AND    riferimento = 'CLIENTI'))
AND    elaborato <> 'S'
Qui ci vedo tre subquries, quegli IN portano il motore a risolvere 
prima le query interne, per poi filtrarle. Troppo.
Vedi se riesci a semplificarle, ti butto giï¿œ una bozza:
WHERE  doc_dett.fk_articolo = articoli.pk_articolo
AND    doc_dett.fk_cliente = :icliente
AND    doc_dett.fk_documento IN(
          SELECT pk_documento
          FROM   documenti
          WHERE  fk_tipodoc = 'ORDINE' OR riferimento = 'CLIENTI'))
AND elaborato <> 'S'
Tieni presente questo:
fk_tipodoc ï¿œ troppo lungo: basta un carattere solo per determinare il 
tipo di documento, stesso discorso per il riferimento: non ï¿œ opportuno 
avere campi che definiscono tipologie con piï¿œ di un carattere, 
rallentano la scansione degli indici e di fatto non servono perchï¿œ la 
loro cardinalitï¿œ ï¿œ molto bassa.
Tu usi le subqueries mediante la clausola IN (subquery) per isolare un 
subset ma ti basta aggiungere una and nella where principale, 
trattandosi di relazioni di join dirette tra tabelle.
Inoltre non vedo l'utilizzo di right e left join, queste potrebbero 
aiutarti a semplificare e allegerire le query.
Ora hai un pï¿œ di materiale su cui lavorare.
Ciao
-- 
Morde


prima di tutto grazie, gli indici sono TUTTI di tipo integer. Detto 
questo vedo di alleggerire ste queries e cosa succede all'applicazione. :)

AND    riferimento = 'CLIENTI'))
il che significa che non essendo indicizzati, il DB scorre 
sequenzialmente *tutti* i migliaia e migliaia di record fisicamente 
esistenti per fare il match con quei valori.
Devi rimediare inizialmente sulla struttura del db, poi aggiungendo gli 
indici corretti. Ad esempio, usa un solo char per decodificare il tipo 
e il riferimento, poi te li metti in un indice, ci guadagni in velocitï¿œ 
di almeno 1000 volte.
Ciao
-- 
Morde

 Morde
Perdonatemi l'intrusione,
potreste spiegare brevemente quali vantaggi potrebbero offrire le
"viste" anzichè le "select su tabelle" in termini di prestazioni, o
sicurezza o altro, che non ne ho la minima idea? Altra cosa, ehm..,
cosa sono le "tabelle in forma normale"?
--
Giovanni


cosa che proverï¿œ a fare sicuramente, grazie :)

scusa morde, poi altra cosa se uso una classe Datamodule con creazione 
di transaction, queries etc. e questa classe viene creata runtime con 
una form, posso *tranquillamente* eliminare la modalitï¿œ modale 
dall'applicazione?
grazie :)

 indice. l'indice ï¿œ il pk della tabella.
Appunto, devono diventarlo! Posta qui sopra il tracciato della tabella 
di definizione documenti.
Quando leggi una query, guarda sempre dove impatta la where.. se non 
hai indici su quei campi il db non ha chance.. deve scorrere 
sequenzialmente tutti i record.. :-O
ciao
-- 
Morde

 posso *tranquillamente* eliminare la modalitï¿œ modale dall'applicazione?
Se tu lanci una query dentro la showmodal, o comunque fai operazioni 
sul db, la showmodal aspetta e si freeza.
Questo e' da sempre cosï¿œ perchï¿œ un'applicazione VCL ï¿œ single thread. E 
la gui della VCL non ï¿œ thread safe.
Ma tu lo sai giï¿œ, visto che il thread ï¿œ partito dalla tua intuizione di 
parallelizzare.
ciao
-- 
Morde

 "viste" anzichï¿œ le "select su tabelle" in termini di prestazioni,
Le viste servono a mostrare una "proiezione" di dati che normalmente 
non sono disponibili tutti insieme, ad esempio la query postata da 
c_thelegend ï¿œ tipicamente una vista.
Le viste vengono tenute aggiornate in tempo reale dal database, tu non 
devi fare nulla, e sono sempre disponibili. Ti tolgono il lavoro di 
dover calcolare tu le query tutte le volte.
Occhio perï¿œ perchï¿œ le viste sono sempre delle query, e in quanto tale 
hanno sempre un costo sulla performance del db.
Le tabelle in forma normale, wikipedia docet: 
http://it.wikipedia.org/wiki/Normalizzazione_del_database
ciao
-- 
Morde


quindi, la risposta ï¿œ no, non esiste un modo sicuro per andare senza la 
modalitï¿œ modale e non incorrere in conflitti, deadlock o quanto meno 
access violation. ovviamente parliamo di VCL, altrimenti il discorso cambia.


CREATE TABLE TIPI_DOC (
   PK_TIPODOC INTEGER DEFAULT 0 NOT NULL,
   TIPO VARCHAR(35) CHARACTER SET ASCII COLLATE ASCII,
   SCARICO VARCHAR(2) CHARACTER SET ASCII COLLATE ASCII,
   PROGRESSIVO VARCHAR(35) CHARACTER SET ASCII COLLATE ASCII,
   RIFERIMENTO VARCHAR(10) CHARACTER SET ASCII COLLATE ASCII,
   PROVVAUTO VARCHAR(1) CHARACTER SET ASCII COLLATE ASCII);
ALTER TABLE TIPI_DOC ADD PRIMARY KEY (PK_TIPODOC);
SET TERM ^ ;
CREATE TRIGGER BI_TIPI_DOC_PK_TIPODOC FOR TIPI_DOC
ACTIVE BEFORE INSERT
POSITION 15
AS
BEGIN
   IF (NEW.PK_TIPODOC IS NULL) THEN
       NEW.PK_TIPODOC = GEN_ID(TIPI_DOC_PK_TIPODOC_GEN, 1);
END^
SET TERM ; ^
trigger incluso :)

 violation. ovviamente parliamo di VCL, altrimenti il discorso cambia.
Non capisco dove vuoi arrivare..
Mi spiego: in un uso normale di un programma, un utente apre un form di 
inserimento dati modale, fa il dataentry, poi salva e chiude il form.
In questo flusso, il caricamento dei dati e la loro persistenza sul db, 
impattano sulla form, vale a dire che se lanci una query che popola un 
combobox e questa query si prende 10 secondi, la form si congela per 30 
secondi.
Ma i dati, in un modo o nell'altro, li devi pur sempre caricare, e 
quindi tu cosa vorresti fare? Mettere in un thread separato il 
caricamento dei dati in modo da non creare ritardi nel momento in cui 
apri il form di inserimento dati?
ciao
-- 
Morde


se parli di una banale anagrafica il discorso regge, ma parliamo di 
statistiche per range di date, subqueries et similia. immaginiamo che il 
risultato di una query impiega un tempo X (diciamo un minuto?) sarebbe 
comodo passare a fare anche altro nel frattempo con la stessa applicazione.

 ALTER TABLE TIPI_DOC ADD PRIMARY KEY (PK_TIPODOC);
aggiungi:
create index ix_tipo on TIPI_DOC;
create index ix_rif on RIFERIMENTO;
Prova e dimmi se hai guadagnato in velocitï¿œ.
Cmq, una pk integer su quella tabella senza che venga usata, a che 
serve? Tu questa tabella la leggi entrando sui campi che ti ho fatto 
creare l'indice.
Ciao
-- 
Morde

 i blob ed userei collegamenti ad i file.
...xche?
La mole di dati e' bassa
Prima di sbatterti sui thread lavora sul db.
1) controlla gli Indici
2) verifica se le query sono scritte in modo da poter usare gli indici
A.

 passare a fare anche altro nel frattempo con la stessa applicazione.
Assolutamente d'accordo con te. Se parliamo di statistiche creati una 
bella classe tthread discendente che genera la reportistica e poi 
gestiscila in parallelo all'attivitï¿œ del tuo programma.
Non legarti al form sennï¿œ in chiusura devi aspettare che finisca il 
thread per deallocare le risorse.
Ciao
-- 
Morde


faccio oggi la prova con dati aggiornati, il pk esiste perchï¿œ l'utente 
ha facoltï¿œ di creare, modificare ed eliminare record in quella tabella, 
tutto per parametrizzazione dall'applicazione.

> cosa sono le "tabelle in forma normale"?
Trovo drammatico che uno che posta qua non sa cosa siano le forme
normali.
A.

        NEW.PK_TIPODOC = GEN_ID(TIPI_DOC_PK_TIPODOC_GEN, 1);
Io non calcolerei la PK su un trigger.
A.


controlli che sto facendo, rimane comunque valido il discorso fatto con 
morde :)
grazie.


motivo per cui il pk � default, cosa (la creazione dei trigger per i pk) 
che ho sempre usato con firebird.

 backup credo.
Dovresti documentarti MOLTO attentamente sui casini che fa NTFS se in
una cartella ci sono piu' di 255 files...
A.

 A.
non hai torto, anzi per ntfs qualunque cosa sia superiore a 255 
caratteri � un problema, solitamente uso cartelle diversificate in base 
alle esigenze.

 normali.
io trovo sconvolgente che persone colte e preparate come te
scrivano tanto per passare il tempo
--
Giovanni

 prestazioni del DB perchï¿œ tali query sono sempre "pronte" all'uso ?
In genere, sï¿œ, ma questo dipende fortemente dall'architettura del tuo 
database, dal disegno, dall'efficenza del motore.
Esempio
Prova ad immaginare di avere un database che contenga eventi di un 
workflow relativo alla fatturazione (presa in carico, pagamento 
effettuato, ritardo sul pagamento, ecc....).
Le tue applicazioni devono frequentemente proporre un elenco in ordine 
temporale degli eventi accaduti, dei loro dettagli e dei dettagli 
relativi alle fatture, ai fornitori, ai pagamenti, e le banche. Per 
questo, tu crei una vista che mettendo in join le tabelle degli eventi 
con le anagrafiche di dominio, esponga il resultset desiderato.
Se questa vista viene letta decine e decine di volte al secondo da 
centinaia di client (immagina una soluzione enterprise web based, un 
CRM), il motore del database l'avrï¿œ ottimizzata e cachata, e quindi 
avrai delle performances molto superiori che ad eseguire per ogni 
client la stessa query.
E' chiaro che se mi crei una vista che mette in join 50 tabelle 
(esagero), questa incide molto sulle performance e va a discapito di un 
approccio piï¿œ flessibile e dinamico fatto invece da piccole e veloci 
query atomiche.
Concludendo, le viste sono uno strumento molto utile per proporre dati 
combinati e di frequente accesso, senza voler demandare il grosso del 
lavoro di querying ai clients. Se hai un motore di database robusto, 
che gira su un server pompato, usa le viste e vedrai che non te ne 
pentirai.
Ciao
-- 
Morde

 scrivano tanto per passare il tempo
Vuoi veramente chre ti risponda?
A.

Grazie Morde,
stanotte provo, poi riferirò.
Buona giornata.


Oltre a tutto quello che ha scritto Morde, io di solito per query 
statistiche uso le storeprocedure (firebird), in modo da lasciare i 
calcoli al motore DB e il client riceve il dataset dei dati.
^_Goblin_^
-- 
Ibis et redibis non morieris in bello
http://goblinland.myminicity.com/



si in effetti.. ma magari le chiama in altro modo, comunque da adesso lo
sapr�.
M.

 calcoli al motore DB e il client riceve il dataset dei dati.
Come per tante altre cose, si passano informazioni incomplete...
Le stored procedure sono sicuramente veloci, riducono il traffico di
rete cosa di cui tanti se ne fregano, e sono centralizzate: se hai
scritto una cazzata non devi aggiornare n client...
Il pezzo mancante e' che centralizzando il carico di lavoro sul server
rischi di impattare negativamente sulle performance generali. Se hai
40 client connessi che lavorano a tutto spiano e fai girare in
contemporanea una stored procedure che si ciuccia qualche milione di
record a meno che non hai un server veramente tosto il sistema
rallenta col risultato che per aggiornare le tue statistiche rallenti
la normali operativita' degli utenti.
Ovviamente se il mio motore db gira su un coso a 64 processori con 4
tera di ram e dischi a 50.000 rpm potrei essere spinto a usare questa
tecnica.,,per poi tozzare nel momento in cui ste cose girano su server
meno potenti...
Ribadisco il concetto che chi suggerisce dovrebbe per dovere di
completezza presentare non solo i pro ma anche i contro...e chi cerca
info dovrebbe chiedere (e chiederSI) il *prezzo* da pagare, xche non
c'e' niente di realmente gratis..
Pax vobiscum
A.

 A.
Quoto in toto e mi scuso, ma al momento mi sembrava implicito che 
eseguendo delle SP che lavorano lato server il carico lo deve supportare 
il server.


 il server.
Goblin, c'e' troppa gente che segue il profeta di turno senza farsi
domande...
Non dare MAI per scontato il buon senso di chi ti ascolta o legge.
A.

> si in effetti.. ma magari le chiama in altro modo,
lasciamo stare, va...
A.


Mi ha molto incuriosito questa affermazione ma, non sono stato capace di 
trovare niente con google, tutte le stringhe di ricerca da me utilizzate 
non hanno sortito risultati.
Potresti postare un link, per favore?
Grazie, ciao.