Newsgroup: it.comp.lang.visual-basic
Titolo: [VB6] Multithread ?

Ciao,
su un progetto VB6 utilizzo un timer "hires" (quello del CCRP) che deve
eseguire del codice con una elevata precisione. Funziona tutto, ma solo se
il programma si limita ad eseguire il codice del timer. Se
contemporaneamente, nello stesso programma, vengono eseguite delle altre
operazioni, il timer "perde colpi", specie se il PC su cui gira ha una CPU
non velocissima: con CPU più grosse il problema si avverte appena.
Vengo al dunque: credo che se potessi separare il codice del timer in un
altro thread, questo inconveniente non ci sarebbe, o verrebbe ridotto di
molto, giusto ?
Visto che VB6 mi pare che non possa fare il multithread, se non con giri
strani, vi chiedo: posso otterere lo stesso risultato creando un EXE o DLL
(ActiveX ?) esterni che contengano e gestiscano il codice del timer ? Tenete
presente che ho continua necessità di interagire con il timer, fornendogli
dal programma principale dei parametri che si dovrà smazzare.
Sono sulla strada giusta o anche così non riuscirei ad ottenere dei
risultati decenti ?
Vi premetto che (mea culpa) non ho mai lavorato con le classi e quindi
sull'argomento conosco solo un po 'di teoria.
Grazie, Paolo

risultati decenti ?
Credo che si meglio fare un servizio e poi magari assegnargli una priorità alta.
I servizi si possono fare anche in VB con qualche API, leggi qui:
http://www.freevbcode.com/ShowCode.asp?ID=4317
--
ciao
  Stefano

 
http://www.freevbcode.com/ShowCode.asp?ID=4317
Grazie Stefano per il link.
Il fatto è che avrei bisogno di avere anche un minimo di interfaccia, ed è
per questo che pensavo ad un eseguibile "a sè stante", e mi pare che i
servizi non possano fornirla. Poi forse ho sbagliato a chiedere nel
messaggio originale informazioni sul multithread, perchè non so neanche se
quella del multithread sia la strada giusta o se ce ne siano di migliori e/o
più semplici.
Provo a semplificare ed a schematizzare il problema:
Un programma in VB6 (Prg 1) deve, tra le altre cose, controllare una porta
seriale: questa seriale comanda l'on/off di un circuito che viene azionato a
seconda dei valori 0 e 1 contenuti in una stringa. Per questa operazione è
richiesta una elevata precisione, che ho ottenuto con un timer "hires" del
CCRP. E funziona bene, se il programma fa solo quello. Quando il programma
esegue qualche altra operazione, il timer si incasina e va "a salti"
fintanto che l'operazione non è conclusa.
Se riuscissi a mettere su un altro exe (Prg 2, quindi un altro task) la
parte del timer/seriale, penso che potrei risolvere il problema.
Ma Prg 2 deve essere controllato da Prg 1, perchè in qualsiasi momento posso
decidere di interrompere il timer o perchè voglio accodare (o eliminare)
dalla stringa binaria dei caratteri.
Riesco in qualche modo ad avere il pieno controllo di un Prg 2 da Prg 1 ?
Paolo

Riesco in qualche modo ad avere il pieno controllo di un Prg 2 da Prg 1 ?
Dovresti attivare e poi disattivare il servizio.
Ma i servizi sono una cosa abbastanza difficile, se puoi evitarli è meglio.
Al limite prova prima con l,API SetPriorityClass a mettere il tuo .exe a
priorità alta in modo da consentirgli di servire correttamente il timer.
Prima però devi ottenere l'handle con GetCurrentProcess.
 Public Declare Function GetCurrentProcess Lib "kernel32" () As Long 
Oltretutto il problema non è la precisione del timer, che è decente anche con il
Timer classico, ma la presenza degli altri threads che anche se tu avessi la
precisione di un nanosecondo ti farebbero aspettare diversi millisecondi prima
di eseguire la routine.
Di seguito SDK dell'Api SetPriorityClass :
____
The SetPriorityClass function sets the priority class for the specified
process. This value together with the priority value of each thread of the
process determines each thread's base priority level.
BOOL SetPriorityClass(
    HANDLE hProcess, // handle to the process
    DWORD dwPriorityClass  // priority class value
   );
Parameters
hProcess
Identifies the process.
Windows NT: The handle must have the PROCESS_SET_INFORMATION access right.
For more information, see Process Objects.
dwPriorityClass
Specifies the priority class for the process. Specify one of the following
values:
Priority Meaning
HIGH_PRIORITY_CLASS Specify this class for a process that performs
time-critical tasks that must be executed immediately. The threads of the
process preempt the threads of normal or idle priority class processes. An
example is Windows Task List, which must respond quickly when called by the
user, regardless of the load on the operating system. Use extreme care when
using the high-priority class, because a high-priority class application can
use nearly all available CPU time.
IDLE_PRIORITY_CLASS Specify this class for a process whose threads run only
when the system is idle. The threads of the process are preempted by the
threads of any process running in a higher priority class. An example is a
screen saver. The idle-priority class is inherited by child processes.
NORMAL_PRIORITY_CLASS Specify this class for a process with no special
scheduling needs.
REALTIME_PRIORITY_CLASS Specify this class for a process that has the
highest possible priority. The threads of the process preempt the threads of
all other processes, including operating system processes performing
important tasks. For example, a real-time process that executes for more
than a very brief interval can cause disk caches not to flush or cause the
mouse to be unresponsive.
Return Values
If the function succeeds, the return value is nonzero.
If the function fails, the return value is zero. To get extended error
information, call GetLastError.
Remarks
Every thread has a base priority level determined by the thread's priority
value and the priority class of its process. The system uses the base
priority level of all executable threads to determine which thread gets the
next slice of CPU time. The SetThreadPriority function enables setting the
base priority level of a thread relative to the priority class of its
process. For more information, see Scheduling Priorities.
See Also
CreateProcess, CreateThread, GetPriorityClass, GetThreadPriority,
SetThreadPriority 
___
--
ciao
  Stefano

 di eseguire la routine.
Questo non è un problema. Non l'ho detto in precedenza, ma la criticità del
timer per non è avere l'intervallo perfetto, quanto avere sempre o stesso
intervallo costantemente nel tempo: ad esempio se imposto 100ms ed il timer
ne fa 105 a me poco importa, vasta che mantenga sempre 105 per tutta la
durata: questo è il mio problema.
Ti ringrazio comunque per le dritte, farò delle prove come mi hai suggerito,
anche se speravo di cavarmela spostando la routine in un exe (o dll)
esterna.
Paolo

esterna.
mmm, meglio che cerchi di capire dove il tuo prog. perde tempo.
--
ciao
  Stefano

 ciò che sta facendo il tuo programma.
Devi scusarmi, forse mi sono spiegato male sin dall'inizio.
Il programma fa *un sacco* di altre cose, anche pesanti, cicli nidificati
inclusi. Ed è proprio durante quelli che nascono i problemi. Ho ottimizzato
al massimo delle mie capacità, ma i problemi rimangono, d'altronde
l'elaborazione che viene fatta è pesante...
E vorrei che "contemporaneamente" (nel senso meno stretto del termine) il
timer continuasse a fare il suo lavoro indisturbato, come se fosse un altro
task.
E così ho pensato che potrei separare la parte più delicata, che ha bisogno
di un "timing" stabile, dal resto dell'elaborazione e di metterla....? dove
? e qui la mia domanda.
Visto che con altri programmi (anche grossi) che girano l'applicazione non
ha problemi, ho pensato che un exe o dll separati (e quindi un task/processo
diverso)  potessero fare al caso. E' però imperativo che il programma
dell'elaborazione sia in grado di passare dei parametri in qualsiasi momento
alla nuova entità (exe o dll o quello che sia). Quindi ActiveX, giusto ?
Paolo

alla nuova entità (exe o dll o quello che sia). Quindi ActiveX, giusto ?
Devi attivare un altro programma e poi passargli dei parametri, attraverso un
file o anche il registro di sistema e le sue API.
Io farei un processo indipendente, non activeX, in modo che sia completamente
asincrono al tuo prog, e al limite possa girare con una priorità più alta.
--
ciao
  Stefano

 asincrono al tuo prog, e al limite possa girare con una priorità più alta.
Cioè mi stai dicendo che, a parte la priorità, un EXE ActiveX non è
completamente asincrono ?
Paolo



La cosa è complessa e anche controversa, leggi qui.
http://shorterlink.com/?N8TJ25
Io per cose così ho usato dei servizi, molto leggeri e fatti inizialmente con il
NTSVC.OCX poi con le api che ti avevo segnalato prima, per la mia avversione
agli ocx.
Credo comunque che se fai due eseguibili indipendenti sei sicuro del fatto che
siano asincroni.
--
ciao
  Stefano

 siano asincroni.
D'accordo, ma per farli "dialogare" dovrei utilizzare API a manetta per
intercettare i messaggi, un vero inferno...
Paolo
P.S: ti ringrazio per il tempo che stai perdendo, cercando di capire il mio
problema e proponendomi delle soulzioni!

problema e proponendomi delle soulzioni!
Non ti preoccupare di questo, in altre occasioni qualcun'altro ha perso il suo
tempo per aiutare me.
--
ciao
  Stefano

 di windows o il registro.
Purtroppo la velocità dei comandi dall'exe1 all'exe2 deve essere pressochè
istantanea, quindi direi che il file non è il più indicato.
Farò delle prove, grazie di tutto!

Farň delle prove, grazie di tutto!
Ok.
--
ciao
  Stefano

 Farò delle prove, grazie di tutto!
Personalmente anch'io in questi casi tenderei ad evitare meccanismi di
sincronizzazione basati su file.
Bye
Raf
-----------------------------------------------------
Funk: cooler than the other side of the pillow.

 richieste) e si comporta di conseguenza.
Mi perdonerai, ma come avrai capito, per me l'argomento ActiveX è totalmente
nuovo: il "server" di cui stai parlando è in pratica lo stesso di cui
abbiamo parlato io e Stefano nel thread oppure la parola "server" indica
qualcos'altro ?
Il client è sempre e solo uno, quindi avrò 1 solo chiamante ed 1 solo
ActiveX Exe attivi in ogni momento su una macchina.
L'idea sarebbe quella di lanciare all'inizio l'ActiveX Exe e di lasciarlo su
fino alla chiusura del chiamante.
Grazie, Paolo

 lasciarlo su fino alla chiusura del chiamante.
È esattamente il funzionamento che intendo io.


ToSka ha scritto:
come ti han già scritto.....
windows non è adatto per applicazioni real-time dove necessiti di
precisione!
Se devi fare ciò....una piccola scheda ed un sistema embedded
probabilmente con un SDK in C...una volta acquisiti i dati...li puoi
processare con tutto il tempo che vuoi!
Infatti è importante acquisire i dati...poi puoi perdere anche qualche
centesimo di secondo a ritrovarli e presentarli!
E' suppergiù quello che fai quando registri la voce con windows....il
DSP della scheda audio processa i segnali e li stocca da qualche
parte....poi l'interfaccia del registratore vocale di windows si occupa
di visualizzare i dati.
Che devi fare?
Semafori stradali?
Oscilloscopio segnali?
Riconoscimento vocale o visivo realtime (antifurto)?


Dunque, ieri sera ho fatto delle prove: ho creato un nuovo ActiveX Exe
(chiamiamolo AX). Al suo interno ho creato una nuova classe (MiaClasse), ed
ho aggiunto una form (Form1, che mi serve unicamente per incollarci il
controllo MSComm per la seriale). Ho anche impostato il riferimento al Timer
CCRP.
Nella classe ho istanziato il timer (Private WithEvents MioTmr As
ccrpTimer), ho definito delle proprietà per impostare la stringa binaria e
per il controllo della velocità, oltre ad un paio di metodi (avviatimer e
fermatimer).
Sulle proprietà del progetto NON ho toccato alcuna impostazione, lasciando
tutto come predefinito, ed ho creato AX.exe
Sul chiamante ho messo: Dim pippo As AX.MiaClasse
e sull'evento load: Set pippo = New AX.MiaClasse
quindi chiamata di tipo "COM".
Su un bottone richiamo il metodo avviatimer e imposto una stringa binaria di
prova.
Su un secondo bottone richiamo un loop esagerato che cicla 1 milione di
volte facendo calcoli in virgola mobile, tanto per tenere seriamente
impegnata la cpu per una decina di secondi.
Risultato: funziona!!! Il timer non si incrucca e prosegue bello sciolto,
mentre il main è impegnato a fare i suoi bei calcoli. Tra l'altro ho messo
dei msgbox sia lato chiamante che lato "server" e NON inchiodano il timer,
che continua come se il fatto non fosse suo...
Viceversa, racchiudendo tutto nel chiamante (stesse identiche routine) il
timer si inchioda appena faccio partire il loop.
Il perchè così funzioni però non l'ho ben capito :-)
Delle impostazioni che mi hai dato stamattina (la prova l'ho fatta ieri
sera) non ne ho rispettata una, lasciando tutto come predefinito:
- Impostare Startup object impostato su "Sub Main": io "Nessuna" (ho VB6 in
italiano)
- Start Mode impostato su Standalone: Modalità di avvio: io Componente
ActiveX
- l'exe viene avviato da solo: io attivazione COM
- Unattended execution: questa non la trovo...
- Threading Model = Thread per object: io Pool di thread (1 thread)
- Il server espone una classe "Controller": non la vedo, probabilmente
dipende dal fatto che le impostazioni precedenti sono diverse.
Però funziona... mah!
Paolo

Riconoscimento vocale o visivo realtime (antifurto)?
Trasmissione codice morse.

 Però funziona... mah!
Ottimo... ma è meglio se pian piano cominci a capire il perché.


ToSka ha scritto:

fammi capire io digito
"Ciao come stai?"
il programma converte in morse e lo spedisce ad un trasmettitore?
oppure lo converte e si deve interfacciare ad un programma che lo manda
lui al trasmettitore?
mi sfugge l'esigenza di avre un timer iper-preciso!

ToSka ha scritto:

forse mi autorispondo......
perchè sei un radioamatore e simili la pressione del tasto con il
pc...e quindi devi generare 2 frequenze diverse?
non sarebbe più semplice allora se prendessi un amico esperto di
elettronica
e ti programmasse un pic per generare le 2 frequenze.....e tu
comunicassi con la parallela....il pic tra le altre cose ha un buffer
per star dietro ai rallentamenti di windows!???
Non sono un esperto di elettronica ma un tempo c'era le schede del
grifo che per pochi euro ti davano un pic completo di generatore
segnali!
sempre...
se ho capito quello che vuoi fare

se ho capito quello che vuoi fare
Direi di si!
Paolo

 Ottimo... ma è meglio se pian piano cominci a capire il perché.
Grande verità!
Grazie, Paolo

 Grazie, Paolo
Di nulla
Ciao



http://www.codeproject.com/vbscript/AsyncSample.asp?df=100&forumid=28225&exp
=0&select=689172
Molto interessanti gli ultimi due, grazie ancora.
Paolo

 quindi non è regolabile finemente.
zac
Allora....
UN SE PO FA!
Almeno non con un pc liscio!
Ma non perchè sia poco buono windows....dubito che ci riusciresti
anche con linux
Perchè?
Perchè hai dei limiti troppo stretti....
40 millisecondi MILLISECONDI
periodi ASSOLUTAMENTE uguali.....in un range strettissimo...se anche
fosse del 10% di 4 ms QUATTRO MILLISECONDI!!!
Se davvero i parametri son questi.....devi andare di scheda
dedicata,...e tu ti interfacci alla scheda per dare solo una
interfaccia grafica!
però io ho trovato questo...non essendo un radioamatore scusami se non
è pertinente
http://www.areasx.com/index.php?D=1&page=articoli.php&id=36&idsezione=
e si parla di azionamento a mano
quindi le tolleranze sembrerebbero abbastanza ampie
a meno che tu non debba collegarti ad un altro radioamatore che abbia
un "qualcosa" ...una scatola nera...che ti costringe a tale
STRETTISSIMA finestra!
Ma ripeto mi sembrano tolleranze impossibili per un normale pc...e cmq
anche belle ardue per una scheda (ossia ci vuole una scheda buona....e
probabilmente costosa!)
una tolleranza di 2 / 3 ms
Ma sei sicuro?
forse se ci spieghi a noi profani quello che vuoi mettere in piedi
forse ti possiamo indicare una via alternativa!
Ad esempio tu parli di seriale......
è più semplice da pilotare una parallela...oltrechè più veloce e
versatile
mi ricordo di quando c'era il programmatore per le schede SECA e si
usava dlportIO
per vedere le porte della parallela!

versatile
Infatti attualmente gestisco sia la seriale che la parallela. Ognuno sceglie
quella che gli pare.
Paolo

Ho letto quasi tutto il thread. Perchè non fai una bella cosa? Creati
due eseguibili. Uno che parlerà costantemente con la seriale
(chiamiamolo dial.exe) e l'altro che fa da interfaccia utente e ti fa
fare quello che ti pare.
Tu puoi solo avviare l'interfaccia utente, che a sua volta lancerà
un'istanza di dial.exe e terrà conto dell'ID del processo. A quel
punto i due dialogano tramite l'API SendMessage(), con la quale
l'interfaccia spedirà al dialer i dati da iniettare nella seriale, e
così facendo bene o male hai tolto dal processo principale il compito
di occuparsi dell'utente e della comunicazione. Nel tuo caso, infatti,
essendoci un unico eseguibile, in ogni istante il processore starà
gestendo o te o il timer, ma più utilizzi l'interfaccia e meno spazio
dai al timer perchè il processo è unico.
Dividendo il tutto in due applicazioni distinte, quando la CPU decide
di passare da un processo all'altro comunque tutto quello che fai con
l'interfaccia non graverà sull'altro processo, perchè ci creerà una
coda di messaggi che verranno soddisfatti quando la cpu tornerà a
badare al processo che ti fa da interfaccia. Oddio, non è che
risolverai al 100% la situazione, ma almeno è un passo...

nella seriale...
Ti premetto che così come non avevo mai lavorato con le classi, non ho mai
neanche lavorato a "basso" livello con le API, se non copiandone qualcuna
beceramente. Ad essere sincero, avevo pensato a questa strada ancor prima di
questo post e dell'ActiveX, ma non sapevo (e non so) bene come fare. Anche
perchè il timer non vorrei che fosse lasciato sempre attivo (forse non
consuma molte risorse, ma come idea non mi piace), ma volevo poterlo
attivare/distaaivare dal chiamante.
Poi ho bisogno di poter modificare l'intervallo in qualsiasi momento.
Inoltre ho la ormai famosa stringa binaria che viene data in pasto al timer
che può anch'essa essere modificata in ogni momento.
Non è finita: devo poter impostare la porta della COM o l'address della
parallela, che si trovano anch'essi nel "dialer"... insomma, devo governare
un bel po' di parametri (alcuni li avevo omessi nei post precedenti). Alcuni
una-tantum al lancio del dialer, altri "on-the-fly".
Veniamo a ldunque: per prendermi l'ID del processo non ho problemi, il
problema è tutto il resto :-).  Non sono in grado di sapere l'hwnd del timer
e soprattutto quali sono i parametri dell'API SendMessage (wMsg, wParam ed
lParam) per attivarlo, disattivarlo, modificarne l'intervallo... poi tieni
conto che sto parlando non del timer di serie, ma del ccRPTimer.
E poi come faccio ad impostare una variabile string con SendMessage ? Questo
proprio non lo immagino.
Ed a modificare la Comport del controllo MSComm ?
Ti sarei davvero grato se potessi darmi qualche dritta... esiste qualche
parte dove si trovano le costanti per wMsg per tutti i controlli ? Eppoi
come faccio a modificare dal chiamante una variabile del chiamato ?
Paolo

C'è un pokino da lavorare, ad essere sincero. Ma non c'è bisogno che
con le API tu cerchi di modificare "direttamente" valori e proprietà
dell'eseguibile asincrono. Con la SendMessage() puoi usare parametri
privati; cioè esiste la costante WM_USER che è un valore oltre il
quale non ci sono messaggi predefiniti di sistema. Tutti i vari
messaggi possibili hanno un valore che non supera la soglia WM_USER
(che vale &H400&), e quindi puoi tranquillamente creare tuoi messaggi
privati partendo da WM_USER+1 in poi.
A questo pro, ti crei delle costanti tue in entrambi i progetti, per
esempio:
Private Const WM_CHANGE_TIMER_TICK = WM_USER + 1
che è il messaggio che dovrai mandare all'altra applicazione quando
vorrai cambiare la velocità del timer, e come lParam e wParam invii i
tuoi dati personali (ad esempio la nuova velocità, in millisecondi,
del timer).
Quindi l'applicazione Dial deve "semplicemente" (quasi, si fa per dire)
intercettare i messaggi WM_XXXX creati da te, controllare i valori e
agire di conseguenza...
Vuoi impostare il timer a 10ms?
SendMessage hWND_DIALER, WM_CHANGE_TIMER_TICK, 10, byval 0&
Ovviamente dal lato "dial" devi creare una sorta di subclassing dei
messaggi. Guarda, sarà un po' difficile all'inizio, ma una volta che
hai imparato e ti sei fatto un modulo tutto tuo che puoi usare come
base, il resto vien da sè...

base, il resto vien da sè...
Ne sono sicuro, ti ringrazio per la soluzione alternativa e per tutte le
info.
Paolo