Pagina 1 di 2 1 2 ultimoultimo
Visualizzazione dei risultati da 1 a 10 su 12

Discussione: [delphi] thread

  1. #1
    Utente di HTML.it
    Registrato dal
    Dec 2002
    Messaggi
    1,315

    [delphi] thread

    Ciao a tutti,

    ho fatto un programma che esegue il download di alcune immagini jpg da internet (fota da una webcam).

    tale operazione dura un po di tempo, ed io vorrei inserire un pulsante per stoppare l'esecuzione di tale download.

    credo (mi sembra di aver capito) che per implementare questa funzionalità devo uilizzare i delphi thread,...ma non riesco a capire bene il concetto di come fare questa cosa che sembrerebbe semplice...

    mi potete indicare qualche esempio...

    grazie

    questo è il codice che eseguo per effettuare il download..
    codice:
    procedure TForm1.BtnScaricaFotoClick(Sender: TObject);
    var
      SourceFile, LocalFile: string;
      ora:TdateTime;
      SR      : TSearchRec;
    begin
      if (varisnull(cxDateEditDal.editvalue)) or (varisnull(cxDateEditAl.editvalue)) then Abort;
    
      try
        screen.Cursor:=crHourGlass;
        try
         CreateDir(ExtractFilePath(Application.ExeName)+'img')
        except
        end;
    
        ora:=cxDateEditDal.Date;
        while ora<=cxDateEditAl.Date do
          begin
           SourceFile := 'http://www.villamia.it/webcam/img_'+FormatDateTime('yyyymmdd',ora)+'_'+FormatDateTime('hhnnss',ora)+'_0.jpeg';
           LocalFile := ExtractFilePath(Application.ExeName)+'img\'+ExtractUrlFileName(SourceFile);
           LblFoto.Caption:='Download File...'+SourceFile;
           LblFoto.Update;
    
           if not FileExists(LocalFile) then
             if URLDownloadToFile(nil, PChar(SourceFile), PChar(LocalFile), 0, nil) = 0 then
              begin
               try
                 cxImageFoto.Picture.LoadFromFile(LocalFile);
                 cxImageFoto.Update;
               except
               end;
              end;
           ora:=IncSecond(ora,1);
          end;
    
        screen.Cursor:=crDefault;
       // ShowMessage('Download eseguito con successo!');
      except
        screen.Cursor:=crDefault;
      //   ShowMessage('Download non eseguito!')
      end
    end;

  2. #2
    Comincia con creare un thread, avviarlo e fermarlo. Dopodiché cerca di gestire più di un thread ed alla fine mettici la funzione che scarica l'immagine. Non è difficile.

  3. #3
    Utente bannato
    Registrato dal
    Dec 2012
    Messaggi
    679
    Sono due richieste diverse.
    Se la domanda è "come fermo il download di qualcosa da internet con un bottone stop" è un conto.

    Se la domanda è "come creo un thread che fa qualcosa, e lo interrompo quando voglio" è argomento diverso.

    Con il corollario "voglio\posso usare componenti di terze parti già pronti"

  4. #4
    Utente bannato
    Registrato dal
    Dec 2012
    Messaggi
    679
    Nel caso "1" ci vuole qualcosa tipo questo
    codice:
    function GetInetFile (const fileURL, FileName: String;i_dimensione:integer=0): boolean;
     const
       BufferSize = 1024;
     var
       hSession, hURL: HInternet;
       Buffer: array[1..BufferSize] of Byte;
       BufferLen: DWORD;
       f: File;
       sAppName: string;
       scaricati:integer;
    
     begin
       frmlog.memlog.lines.clear;
       frmlog.memlog.lines.add('Scaricamento da internet (si può premere STOP)');
       if i_dimensione>0 then
           frmlog.memlog.lines.add(      'Byte da trasferire '+(numeroconpuntini(i_dimensione)));
       frmlog.show;
       salvabtnstop;
       frmlog.btnstop.enabled:=true;
       application.processmessages;
    
       if i_dimensione>0 then
           frmlog.pgrprogressototale.max:=i_dimensione;
    
      deletefile(pchar(filename));
      result := false;
      scaricati:=0;
      sAppName := ExtractFileName(Application.ExeName) ;
      hSession := InternetOpen(PChar(sAppName), INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0) ;
      try
       hURL := InternetOpenURL(hSession, PChar(fileURL), nil, 0, 0, 0) ;
       try
        AssignFile(f, FileName) ;
        Rewrite(f,1) ;
        repeat
           if not frmlog.btnstop.enabled then break;
    function GetInetFile (const fileURL, FileName: String;i_dimensione:integer=0): boolean;
     const
       BufferSize = 1024;
     var
       hSession, hURL: HInternet;
       Buffer: array[1..BufferSize] of Byte;
       BufferLen: DWORD;
       f: File;
       sAppName: string;
       scaricati:integer;
    
     begin
       frmlog.memlog.lines.clear;
       frmlog.memlog.lines.add('Scaricamento da internet (si può premere STOP)');
       if i_dimensione>0 then
           frmlog.memlog.lines.add(      'Byte da trasferire '+(numeroconpuntini(i_dimensione)));
       frmlog.show;
       salvabtnstop;
       frmlog.btnstop.enabled:=true;
       application.processmessages;
    
    
    
       if i_dimensione>0 then
           frmlog.pgrprogressototale.max:=i_dimensione;
    
      deletefile(pchar(filename));
      result := false;
      scaricati:=0;
      sAppName := ExtractFileName(Application.ExeName) ;
      hSession := InternetOpen(PChar(sAppName), INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0) ;
      try
       hURL := InternetOpenURL(hSession, PChar(fileURL), nil, 0, 0, 0) ;
       try
        AssignFile(f, FileName) ;
        Rewrite(f,1) ;
        repeat
    
    //// ECCOLO QUI
           if not frmlog.btnstop.enabled then break;
    /////////////////
         InternetReadFile(hURL, @Buffer, SizeOf(Buffer), BufferLen) ;
         BlockWrite(f, Buffer, BufferLen);
           if i_dimensione>0 then
           begin
               frmlog.memlog.lines[2]:=  'Scaricamento       '+(numeroconpuntini(scaricati))+' bytes';
               frmlog.pgrprogressototale.position:=scaricati;
           end
           else
           frmlog.memlog.lines[1]:=      'Scaricamento '+trim(numeroconpuntini(scaricati))+' bytes';
    
           application.processmessages;
        scaricati:=scaricati+bufferlen;
           application.processmessages;
        until BufferLen = 0;
        CloseFile(f) ;
        result := True;
       finally
        InternetCloseHandle(hURL)
       end
      finally
       InternetCloseHandle(hSession)
      end;
       if not frmlog.btnstop.enabled then
       begin
           deletefile(pchar(filename));
       end;
    
      frmlog.btnstop.enabled:=true;
      ripristinabtnstop;
       frmlog.hide;
     end;
    
    
         InternetReadFile(hURL, @Buffer, SizeOf(Buffer), BufferLen) ;
         BlockWrite(f, Buffer, BufferLen);
           if i_dimensione>0 then
           begin
               frmlog.memlog.lines[2]:=  'Scaricamento       '+(numeroconpuntini(scaricati))+' bytes';
               frmlog.pgrprogressototale.position:=scaricati;
           end
           else
           frmlog.memlog.lines[1]:=      'Scaricamento '+trim(numeroconpuntini(scaricati))+' bytes';
    
           application.processmessages;
        scaricati:=scaricati+bufferlen;
           application.processmessages;
        until BufferLen = 0;
        CloseFile(f) ;
        result := True;
       finally
        InternetCloseHandle(hURL)
       end
      finally
       InternetCloseHandle(hSession)
      end;
       if not frmlog.btnstop.enabled then
       begin
           deletefile(pchar(filename));
       end;
    
      frmlog.btnstop.enabled:=true;
      ripristinabtnstop;
       frmlog.hide;
     end;
    E' un po' brutale, ma penso si capisca

  5. #5
    Utente di HTML.it
    Registrato dal
    Dec 2002
    Messaggi
    1,315
    Originariamente inviato da franzauker2.0
    Sono due richieste diverse.
    Se la domanda è "come fermo il download di qualcosa da internet con un bottone stop" è un conto.

    Se la domanda è "come creo un thread che fa qualcosa, e lo interrompo quando voglio" è argomento diverso.
    ciao,
    io pensavo fosse la stessa cosa..., e comunque io ho bisogno di premere un pulsante che mi ferma un processo. sia se questo è il download di un file da internet e sia se è il ciclo di elaborazione di una determinata operazione.
    in linea generale vorrei capire, una volta per tutte, come funziona questo tipo di funzionamento del thread per poterlo inserire nelle mie procedure dove l'esecuzione di alcuni passi possa essere interrotto dall'operatore.

    sto cercando di capire l'esempio che (con tanti ringraziamenti) hai postato...non mi sono chiare alcune cose:

    fai riferimento ad un form frmlog in cui c'e' un btnstop ed un memo
    poi fai riferimento a delle procedure salvabtnstop ripristinabtnstop che non so cosa facciano..

    grazie

  6. #6
    Utente bannato
    Registrato dal
    Dec 2012
    Messaggi
    679
    Originariamente inviato da 123delphi321
    ciao,
    io pensavo fosse la stessa cosa..., e comunque io ho bisogno di premere un pulsante che mi ferma un processo. sia se questo è il download di un file da internet e sia se è il ciclo di elaborazione di una determinata operazione.
    in linea generale vorrei capire, una volta per tutte, come funziona questo tipo di funzionamento del thread per poterlo inserire nelle mie procedure dove l'esecuzione di alcuni passi possa essere interrotto dall'operatore.

    sto cercando di capire l'esempio che (con tanti ringraziamenti) hai postato...non mi sono chiare alcune cose:

    fai riferimento ad un form frmlog in cui c'e' un btnstop ed un memo
    poi fai riferimento a delle procedure salvabtnstop ripristinabtnstop che non so cosa facciano..

    grazie
    E' una banale form di log di testo, con dentro il bottone da premere.

    Confermo che sono due "cose" estremamente diverse, così come la richiesta, e manca il riferimento all'utilizzo di componenti.

    Andiamo per ordine: supponiamo di voler fermare un ciclo, qualsiasi
    (un while ad esempio). Un metodo (non "il", ma uno) è quello di utilizzare un componente visuale, ad esempio un bottone, che viene posto in stato enabled=true all'inizio del ciclo.
    Dentro il ciclo verifichi se il bottone è enabled: se NON lo è => esci
    L'evento del bottone (onclick) sarà, ovviamente, t[speed]button(sender).enabled:=FALSE;

    "Ogni tanto" poi ti servirà, nel ciclo, un application.processmessages (in realtà si può ottenere lo stesso effetto in modi diversi, ma certamente tanti esperti ti spiegheranno quali).
    Questo forzerà la rilettura della coda dei messaggi windows che sono "parcheggiati" nella form (in sostanza la pressione del bottone).
    All'uscita dal ciclo testi se il bottone è enabled: se non lo è => è stato premuto stop e quindi prendi in carico con qualcosa tipo "esecuzione terminata cazzi tuoi".

    Puoi aumentare la velocità\aumentare la reattività del bottone STOP a seconda di quanto spesso fai fare il processmessages: più frequenti => più lento.
    Un modo "normale" è quello di farlo ogni tot chunk di ciclo

    chunk:=numerocicli div 100;

    ...
    if (iterazione mod chunk)=0 then
    begin
    application.processmessages;
    if NOT bottonestop.enabled then break;
    end;

    Altri approcci sono con un TTIMER, il quale a sua volta crea un vero e proprio thread.

    ===
    Breve introduzione ai thread. Si tratta di pezzi di programma che vengono lanciati parallelamente al thread principale.
    Si creano abbastanza facilmente, li si lancia, li si può fermare ("uccidendoli"), abbastanza facilmente anche con la libreria standard.

    Quello che non è esattamente banale è sincronizzare i thread tra di loro e, in particolare, l'accesso a risorse condivise, che sono tipicamente i componenti VCL (visuali) e le eventuali strutture dati condivise (chessò un array globale).

    Anche qui la faccenda è diverse se vuoi semplicemente fare un thread, e bloccarlo, oppure vuoi sfruttare più thread contemporanei (ad es. più core/CPU).

    Nel primo caso la situazione è più semplice, in quanto tipicamente la sequenza del thread principale è
    ... crea thread
    ... lancia thread
    ... FAI QUALCOSA (qui c'è il problema, vedi poi)
    ... sincronizzati col thread terminato.

    Il "fai qualcosa" è il problema, nel senso che puoi mettere a dormire il thread principale, ma devi agire a livello molto basso (non-VCL per capirci), oppure in questo ti serve un ciclo scemo che fa una sorta di busy waiting con l'oppurtuna istruzione per rilasciare rapidamente la CPU in idle.

    A livello maggiore di complessità c'è la sincronizzazione vera e propria di thread diversi che, per quanto mi riguarda, uso solo nel caso in cui davvero serva un'elaborazione parallela (ad esempio calcolo di hash su file diversi, quanto piuttosto elaborazione di liste di dati tra di loro indipendenti).

    Per questi lavori (ossia "veri") tipicamente utilizzo una libreria (ce ne sono tante) che rendono più agevole operare con tali oggetti.

  7. #7
    Originariamente inviato da franzauker2.0
    Altri approcci sono con un TTIMER, il quale a sua volta crea un vero e proprio thread.
    Non so se sia cambiato qualcosa recentemente, ma credo che TTimer si basi sul messaggio WM_TIMER (viene creata una finestra nascosta), non su un thread parallelo.

  8. #8
    Utente di HTML.it
    Registrato dal
    Dec 2002
    Messaggi
    1,315
    ho utilizzato il primo metoto, che per il momento, sembra risolvere il mio problema...


    codice:
    procedure TForm1.BtnScaricaFotoClick(Sender: TObject);
    var
      SourceFile, LocalFile: string;
      ora:TdateTime;
      SR      : TSearchRec;
    begin
    ...
    ...
        BtnStop.Enabled:=True;
        BtnScaricaFoto.Enabled:=False;
        ora:=cxDateEditDal.Date;
        while ora<=cxDateEditAl.Date do
          begin
           application.processmessages;
           if not BtnStop.Enabled then break;
           SourceFile := 'http://www.villamia.it/webcam/img_'+FormatDateTime('yyyymmdd',ora)+'_'+FormatDateTime('hhnnss',ora)+'_0.jpeg';
           LocalFile := ExtractFilePath(Application.ExeName)+'img\'+ExtractUrlFileName(SourceFile);
           LblFoto.Caption:='Download File...'+SourceFile;
           LblFoto.Update;
    
           if not FileExists(LocalFile) then
             if URLDownloadToFile(nil, PChar(SourceFile), PChar(LocalFile), 0, nil) = 0 then
              begin
               try
                 cxImageFoto.Picture.LoadFromFile(LocalFile);
                 cxImageFoto.Update;
               except
               end;
              end;
           ora:=IncSecond(ora,1);
          end;
    ....
    ....
    
    procedure TForm1.BtnStopClick(Sender: TObject);
    begin
      BtnStop.Enabled:=False;
      BtnScaricaFoto.Enabled:=True;
    end;
    grazie

  9. #9
    Utente bannato
    Registrato dal
    Dec 2012
    Messaggi
    679
    Originariamente inviato da ESSE-EFFE
    Non so se sia cambiato qualcosa recentemente, ma credo che TTimer si basi sul messaggio WM_TIMER (viene creata una finestra nascosta), non su un thread parallelo.
    non puoi usare thread separato il timer serve per il processmessages del thread principale

  10. #10
    Utente di HTML.it
    Registrato dal
    Dec 2002
    Messaggi
    1,315
    ciao,

    usando il primo metodo funziona bene...
    ma verificando i vari 'application.processmessages' l'utente potrebbe navigare nel menu della applicazione e potrebbe attivare altre procedure.

    penso che sia necessario evitare che questo accada ed è per questo motivo che, forse, franzauker sovrappone una FormLog alla applicazione 'costringendo' l'utente ad attendere la fine dell'elaborazione o la pressione del tasto 'stop'

    per franzauker,
    ho letto bene questo problema?
    o è meglio lasciare l'utente libero di attivare altre procedure?


    grazie

Permessi di invio

  • Non puoi inserire discussioni
  • Non puoi inserire repliche
  • Non puoi inserire allegati
  • Non puoi modificare i tuoi messaggi
  •  
Powered by vBulletin® Version 4.2.1
Copyright © 2024 vBulletin Solutions, Inc. All rights reserved.