2011-02-01 11 views
6

Ho una macchina fotografica e sto leggendo le immagini in tempo reale in un array. Sto applicando qualche algoritmo all'immagine e mostrandola. Quindi ottengo l'immagine successiva e la visualizzo. Quindi sto trasmettendo le immagini dalla fotocamera al display. Tuttavia, voglio anche salvare le immagini sul disco rigido dopo averle visualizzate. Ho provato ad usare il thread principale ma tutto ha rallentato troppo. Ho quindi provato a usare ThreadPool (vedi il codice sotto). Questo non rallenta lo schermo ma ho trovato che le immagini non vengono salvate correttamente. Sembra che non siano nell'ordine previsto e dopo che sono state salvate circa 50 immagini, i successivi dati dell'immagine appaiono confusi. Immagino che vengano avviati troppi thread.Come si salvano i file sul disco rigido in un thread separato?

C'è un modo migliore per farlo? Penso di aver bisogno di un solo thread per salvare le immagini. Forse una specie di coda che salva ogni immagine in sequenza. Fintanto che è fatto in background e non rallenta il display. Se qualcuno potesse pubblicare uno snippet di codice sarebbe fantastico.

short[] image1 = new short[20000]; 
while(streaming) 
{ 
    ReadImageFromCamera(ref image1) 
    ImageData data;  

    data.fileName = imageNumber; 
    data.image = image1; 

    ThreadPool.QueueUserWorkItem(WriteImageToFile, data); // Send the writes to the queue 
} 


private void WriteImageToFile(object imageData) { 

    try { 
     ImageData data = (ImageData)imageData; 
     System.Runtime.Serialization.Formatters.Binary.BinaryFormatter bf = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter(); 

     string fName = myDirectory + @"/" + Convert.ToString(data.fileName) + @".spe"; 

     using (Stream myStream = new FileStream(fName, FileMode.Create)) { 
      bf.Serialize(myStream, data.image); 
     } 
    } 
    catch (Exception) { } 
} 
+0

corto [] image1 = nuovo abbreviazione [20000]; Sono sicuro che tu sia sicuro che la tua immagine non richieda più di quei 40000 byte. –

+0

'ReadImageFromCamera' copia i dati nell'array passato o crea un nuovo array assegnato a' image1'? –

+0

sì, mi dispiace solo per l'illustrazione. Le immagini variano in dimensioni a seconda delle impostazioni dell'utente (ma non una volta che lo streaming è iniziato). Sto leggendo tutti i dati dell'immagine OK quindi nessun problema lì. – Doug

risposta

0

Quando si tratta di thread, l'ordine non è più sotto il proprio controllo. Il pool di thread può scegliere di pianificare i thread in qualsiasi ordine desiderato. Se hai bisogno che le cose accadano in sequenza in un ordine specifico, il threading non ha molto senso comunque.

Per quanto riguarda le immagini danneggiate, sembra che l'istanza short[] image1 venga passata in giro. Non è chiaro cosa succede all'interno di ReadImageFromCamera, ma poiché si passa un array preinizializzato in esso, è probabile che il metodo utilizzerà tale array e semplicemente copi i dati in esso (anche se la parola chiave ref indica che potrebbe creare un nuovo array istanza e assegnarlo invece). Quindi si passa quell'istanza dell'array a WriteImageToFile su un thread separato.

Nel frattempo, in parallelo, si ottiene l'immagine successiva. Ora si dispone di uno scenario in cui ReadImageFromCamera potrebbe scrivere dati nell'array nello stesso momento in cui WriteImageToFile sta memorizzando i dati su disco. Lì hai la tua immagine corrotta. Questo può essere evitato facendo passare una nuova istanza array per WriteImageToFile:

ReadImageFromCamera(ref image1) 
ImageData data;  

data.fileName = imageNumber; 
data.image = (short[])image1.Clone(); // create a new array instance, so that 
             // the next call to ReadImageFromCamera 
             // will not corrupt the data 

ThreadPool.QueueUserWorkItem(WriteImageToFile, data); 

Eppure, as has been mentioned by Al Kepp, dal momento che si dispone di un solo disco rigido, il lancio di tanti fili potrebbe non essere la soluzione migliore qui. Si può avere un thread separato di lunga durata per la memorizzazione dei dati sul disco e l'inserimento delle immagini in una sorta di coda che il thread di archiviazione preleva e scrive sul disco. Ciò comporta una serie di problemi relativi alla concorrenza, che limitano le dimensioni della coda e quali no.

+0

grazie per quello. – Doug

+0

Preferirei creare un nuovo array short [] invece di chiamare Clone(). Voglio dire creare un nuovo corto [20000] giusto prima di chiamare ReadImageFromCamera(). Non è Clone() inutilmente lento per questo scenario? Perché dovremmo creare un clone quando butteremo via l'originale subito dopo. –

6

Penso che dovresti evitare di iniziare un nuovo thread per ogni immagine particolare. Dato che hai un solo disco fisso e archivi tutti i file nella singola directory, dovresti usare solo un thread di scrittura su disco. Quindi mi consiglia di utilizzare qualche coda concorrente per trasferire i lavori dal thread della fotocamera al thread del writer. Non mostro lo "snippet di codice" perché non è una cosa che puoi scrivere in buona qualità in poche righe di codice.

Inoltre, in alcuni casi è necessario inserire "nuovo breve [20000]" per ogni immagine, altrimenti viene sovrascritto dall'immagine successiva prima di salvarlo sul disco.

Inoltre, mi aspetto che sia sufficiente scrivere file nel thread principale, perché Windows utilizza tecniche concorrenti (principalmente cache del disco) automaticamente quando si scrivono i dati sul disco. Sei sicuro che il tuo hardware sia abbastanza veloce da scrivere tutti quei dati in tempo reale?

+0

Ho provato a scrivere dati su disco nel thread principale, ma ha rallentato notevolmente il display. La soluzione di threading non rallenta il display – Doug

0

È necessario creare un buffer distinto per il thread da cui leggere i dati, altrimenti il ​​thread principale lo sovrascriverà quando si esegue il dump su un file. Il modo in cui lo fai sembra copiare solo riferimenti (image1 in particolare).

Quindi:

ThreadPool.QueueUserWorkItem(WriteImageToFile, data); 

al posto dei dati si invia in una copia profonda di data. Poiché sembra che lo stiate già facendo - ma nel thread di lavoro - è sufficiente spostare la copia prima dell'invio dello.

HTH

+2

Una copia superficiale sarà abbastanza. Poiché gli elementi sono di un tipo di valore immutabile, non è possibile che vengano modificati in modo tale da compromettere un riferimento dell'array clonato. –

+0

@Fredrik: la copia superficiale è sufficiente. +1 Ma la ragione non è l'immutabilità degli elementi, perché l'array nel suo complesso non è immutabile. Rimane intatto in questo scenario: viene solo visualizzato e salvato su disco, quindi non c'è motivo per fare una copia profonda. –

+0

@Fredrik se una copia superficiale è sufficiente, perché la si clona nella risposta? – Simone

0

Devi controllare prima di pensare le discussioni se la velocità di un normale disco sarà sufficiente per il vostro compito, come si può creare immagini più veloce di scrittura sul disco. Se la creazione dell'immagine è più veloce della scrittura, guarderei l'utilizzo di un disco di memoria, ma poi devi calcolare se la dimensione è sufficiente finché non interrompi la fotocamera, in modo che tu possa scrivere sul disco normale durante la notte.
Se si utilizza .NET 4.0, suggerirei di utilizzare Concurrent queue insieme a un thread normale (poiché il thread verrà eseguito fino al termine del programma).

0

È possibile eseguire le seguenti operazioni.

public class AsyncFileWriter 
    { 
     private readonly FileStream fs; 
     private readonly AsyncCallback callback; 
     public Action FinishedCallback; 
     private IAsyncResult result; 
     private class AsyncState 
     { 
      public FileStream Fs; 

     } 

     private void WriteCore(IAsyncResult ar) 
     { 
      if (result != null) 
      { 
       FileStream stream = ((AsyncState)ar.AsyncState).Fs; 
       stream.EndWrite(result); 
       if (this.FinishedCallback != null) 
       { 
        FinishedCallback(); 
       } 
      } 
     } 

     public AsyncFileWriter(FileStream fs, Action finishNotification) 
     { 
      this.fs = fs; 
      callback = new AsyncCallback(WriteCore); 
      this.FinishedCallback = finishNotification; 
     } 

     public AsyncFileWriter(FileStream fs) 
      : this(fs, null) 
     { 

     } 


     public void Write(Byte[] data) 
     { 
      result = fs.BeginWrite(data, 0, data.Length, callback, new AsyncState() { Fs = fs }); 
     } 
    } 

Più tardi puoi consumarlo come.

static void Main(string[] args) 
     { 
      FileStream fs = File.Create("D:\\ror.txt"); 
      ManualResetEvent evt = new ManualResetEvent(false); 
      AsyncFileWriter writer = new AsyncFileWriter(fs,() => 
                   { 
                    Console.Write("Write Finished"); 
                    evt.Set(); 
                   } 

       ); 
      byte[] bytes = File.ReadAllBytes("D:\\test.xml");//Getting some random bytes 

      writer.Write(bytes); 
      evt.WaitOne(); 
      Console.Write("Write Done"); 
     } 
0

modo rapido e sporco sta iniziando singolo nuovo thread e il lavoro con i membri della classe a livello mondiale - il nuovo thread dovrebbe essere in grado di accedervi, mentre il thread principale li aggiorna.

Prima di tutto, sono queste linee al di fuori di ogni funzione:

private List<ImageData> arrGlobalData = new List<ImageData>(); 
private bool keepWritingImages = true; 

Ora modificare il codice nel thread "principale" per questo:

short[] image1 = new short[20000]; 
ThreadPool.QueueUserWorkItem(WriteImageToFile, null); 
while(streaming) 
{ 
    ReadImageFromCamera(ref image1) 
    ImageData data = new ImageData(); 
    data.fileName = imageNumber; 
    data.image = image1; 
    arrGlobalData.Add(data); 
} 
keepWritingImages = false; 

E infine avere tale funzione per la nuovo thread:

private void WriteImageToFile(object imageData) 
{ 
    while (keepWritingImages) 
    { 
     if (arrGlobalData.Count > 0) 
     { 
      ImageData data = arrGlobalData[0]; 
      try 
      { 
       System.Runtime.Serialization.Formatters.Binary.BinaryFormatter bf = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter(); 
       string fName = myDirectory + @"/" + Convert.ToString(data.fileName) + @".spe"; 
       using (Stream myStream = new FileStream(fName, FileMode.Create)) 
       { 
        bf.Serialize(myStream, data.image); 
       } 
      } 
      catch 
      { 
      } 
      finally 
      { 
       arrGlobalData.Remove(data); 
      } 
     } 

     Thread.Sleep(10); 
    } 
} 
+1

Non riesco a credere quanto sia buono questo sito. È fantastico, grazie per tutte le risposte. – Doug

+0

Non riesco a credere quanto sia buono questo sito. È fantastico, grazie per tutte le risposte. La risposta di Frederick Mork sembra aver risolto tutto. data.image = (short []) image1.Clone(); – Doug

+0

Shadow Wizard Ho intenzione di provare il tuo codice come Im ancora un po 'preoccupato QueueUserWorkItem utilizzerà troppi thread ad un certo punto. – Doug

Problemi correlati