2012-03-19 5 views
5

Sto programmando un'app multi-thread. Ho due fili. On è per il trasferimento di alcuni dati dal dispositivo al buffer di dati globali e il secondo è per scrivere quei dati su file.La best practice su come aspettare i dati all'interno del thread e scriverli in un file?

I dati dal dispositivo al buffer si stanno trasferendo in modo asincrono. Lo scopo del secondo thread dovrebbe essere quello di attendere la quantità specificata di dati da scrivere nel buffer dei dati principali e, infine, di scriverli in un file.

Bene, il primo thread è in DLL e il secondo è nell'app principale. Temporaneamente risolvo questo con gli eventi. Il primo thread trasferisce i dati dal dispositivo al buffer dei dati principali e conteggia i dati e quando viene trasferita la quantità specificata di dati imposta un evento. Il secondo attende che l'evento sia segnalato e quando viene eseguito un codice per l'archivio dati. Semplice come quello che sta funzionando.

Thread1.Execute: 

    var DataCount, TransferedData: Integer; 

    DataCounter := 0; 
    while not Terminted do 
    begin 
    TransferData(@pData, TransferedData); 
    Inc(DataCounter, TransferedData) 
    if DataCounter >= DataCountToNotify then SetEvent(hDataCount); 
    end; 



    Thread2.Execute: 

    hndlArr[0] := hDataCount; 
    hndlArr[1] := hTerminateEvent; 

    while (not Terminated) do 
    begin 
    wRes := WaitForMultipleObjects(HandlesCount, Addr(hndlArr), false, 60000); 
    case wRes of 
     WAIT_OBJECT_0: 
     begin 
      Synchronize(WriteBuffer);     // call it from main thread 
      ResetEvent(hndlArr[0]); 
     end; 
     WAIT_OBJECT_0 + 1: 
     begin 
      ResetEvent(hTerminateEvent); 
      break; 
     end; 
     WAIT_TIMEOUT: Break; 
    end; 
    end; 

Ora vorrei fare secondo thread più indipendente ... in modo da poter rendere più istanze di secondo thread e non ho bisogno di aspettare per il primo thread. Vorrei spostare i dati contando parte del codice dal primo thread al secondo, quindi non avrò più bisogno del conteggio dei dati nel primo thread. Il primo sarà solo per scopi di trasferimento dati.

Vorrei utilizzare il secondo come contatore di dati e memorizzare i dati. Ma ora dovrò fare un ciclo e controllare continuamente manualmente la quantità di dati specificata. Se avessi il ciclo while dovrò aggiungere un po 'di sonno in modo che il secondo thread non ridurrà le prestazioni del computer, ma non so per quanto tempo dovrebbe dormire mentre la velocità di trasferimento dei dati nel primo thread non è costante e quindi la velocità di conteggio nella seconda discussione varierà.

mia ipotesi che questo esempio di codice non è buono:

Thread2.Execute: 
    var DataCount: Integer; 
    DataIdx1 := GetCurrentDataIdx; 
    while (not Terminated) do 
    begin  
    if (GetCurrentDataIdx - DataIdx1) >= DataCountToNotify then 
    begin 
     Synchronize(WriteBuffer); 
     DataIdx1 := GetCurrentIdx; 
    end; 
    sleep(???); 
    end; 

Quindi la mia domanda è che cosa è l'approccio migliore per risolvere il problema con il conteggio dei dati e la memorizzazione nel secondo thread? Quali sono le tue esperienze e i tuoi suggerimenti?

+3

Perché imponi la scrittura del file sul thread principale, 'Sincronizza (WriteBuffer)'? Non capisco come riuscirai a moltiplicare il secondo thread e mantenere l'ordine di scrittura in un singolo file. –

+0

Il thread verrà moltiplicato ma i dati verranno archiviati in file diversi. Non ho scritto da nessuna parte che lo scriverò nello stesso file. Il mio obiettivo è avere più controlli che siano in grado di memorizzare i dati dalla stessa fonte in file diversi. – Nix

+0

È bene sapere, poiché la risposta dipenderà da queste informazioni. Mi chiedo ancora perché la scrittura del file debba essere applicata al thread principale. –

risposta

5

Hai alcuni problemi. @LU RD ne ha già indicato uno - non sincronizzare elementi che non devono essere sincronizzati. Non è chiaro cosa faccia "WriteBuffer", ma il file system e tutti i database che ho usato vanno bene per avere un thread che apre un file/tabella e scrive su di essi.

Il vostro sistema tampone potrebbe probabilmente fare attenzione. Esiste un po 'di "quantità di dati specificata" o si tratta di una cifra nozionale che consente la scrittura pigra?

In genere, i thread dei produttori e dei consumatori scambiano più puntatori buffer sulle code e quindi evitano di condividere qualsiasi singolo buffer. Dato che si tratta di una DLL e quindi le chiamate di gestione della memoria possono essere problematiche, probabilmente le eviterei anche creando un pool di buffer all'avvio per trasferire i dati attorno al sistema. Vorrei usare una classe di buffer anziché solo dei puntatori alla memoria, ma non è assolutamente necessario (solo molto più facile/flessibile/sicuro).

I cicli Sleep() sono un modo spettacolare di comunicare tra i thread. Sleep() ha i suoi usi, ma questo non è uno di questi. Delphi/Windows ha un sacco di meccanismi di sincronizzazione - eventi, semafori, mutex, ecc - che rendono inutile tale sondaggio.

LU RD ha anche menzionato i problemi dell'elaborazione parallela dei dati di cui è necessario conservare l'ordine. Questo spesso richiede ancora un altro thread, una raccolta in stile elenco e numeri di sequenza. Non proverei a farlo finché non avrai le comunicazioni inter-thread funzionanti.

+0

Il codice superiore è solo per scopi dimostrativi. Non preoccuparti di sincronizzare. Ho già sistemato le cose e uso il meccanismo corretto per sincronizzare i thread ecc. Come ho detto, vorrei contare i dati e poi quando la condizione si applica ad es. DataCountToNotify = 8192 Vorrei memorizzare i dati. Ma vorrei evitare di dormire. C'è qualche altra alternativa come farlo? – Nix

+0

@Nix se non mi sbaglio, fintanto che il tuo thread fa "qualcosa" nel ciclo while, puoi saltare il "sonno", ma puoi usare sleep (1) che è una sospensione di qualsiasi posizione tra 1 e 55 ms (win xp ha un problema, non sono sicuro delle versioni future) – ComputerSaysNo

+1

@DorinDuminica, vedere [thread-sleep-is-un-segno-di-un-mal progettato-programma] (https://msmvps.com/ blog/peterritchie/archive/2007/04/26/thread-sleep-is-a-sign-of-a-poor-designed-program.aspx) per argomenti contro l'uso di 'Sleep' in una discussione. –

2

Se si desidera evitare la chiamata Sleep() nel secondo thread, utilizzare un timer attendibile come TSimpleEvent.

Impostare il tempo di sospensione per gestire tutte le condizioni di cronometraggio. C'è un vantaggio nell'usare questo schema al posto di un normale Sleep(), poiché un timer non aspetta il thread in un sonno profondo.

Per smaltire il filo, vedere i commenti nel codice.

var 
    FEvent: TSimpleEvent; 
    FSleepTime: Integer = 100; // Short enough to handle all cases 

Constructor TThread2.Create; 
begin 
    Inherited Create(False); 
    FEvent := TSimpleEvent.Create; 
    Self.FreeOnTerminate := True; 
end; 

procedure TThread2.Execute; 
var 
    DataCount: Integer; 
begin 
    DataIdx1 := GetCurrentDataIdx; 
    while (fEvent.WaitFor(FSleepTime) = wrTimeout) do 
    begin 
    if Terminated then 
     break; 
    // Do your work 
    if (GetCurrentDataIdx - DataIdx1) >= DataCountToNotify then 
    begin 
     // Write data to buffer 
     DataIdx1 := GetCurrentIdx; 
    end; 
    end; 
end; 

// To stop the thread gracefully, call this instead of Terminate or override the DoTerminate 
procedure TThread2.SetTerminateFlag; 
begin 
    FEvent.SetEvent; 
end; 
+0

LU RD, grazie, sembra molto promettente. Dovrò variare il tempo di FSleep a seconda anche della velocità di trasferimento dei dati nel primo thread. Posso calcolare quel tempo. Come ho detto, cerco di evitare di dormire in thread, ecco perché stavo cercando un'alternativa migliore per dormire. Non ho mai usato il sonno in nessun thread che ho programmato. Non l'ho trovato molto sicuro. Mi piace che il sistema sia più determinato. Molte grazie. Proverò a realizzarlo nel modo che hai suggerito. – Nix

+0

Questo è ragionevole al primo sguardo, ma non lo è. Intendevi "wrSignaled" invece di "wrTimeout"? Inoltre, c'è ancora la chiamata Sycnhronize qui che rende il thread peggiore di inefficace - in realtà introdurrà più delay. –

+0

@MartinJames, il Synchronize è evidentemente sbagliato, ma l'OP ha detto che questo non era più un problema. Il ciclo continuerà a funzionare anche se non viene segnalato (ogni millisecondo di FSleepTime). Questo sembra ok nella mia mente. –