2011-02-01 2 views
6

У меня есть камера, и я читаю изображения в реальном времени в массив. Я применяю некоторый алгоритм к изображению и отображаю его. Затем я получаю следующее изображение и отображаю его. Поэтому я передаю изображения с камеры на дисплей. Однако я также хочу сохранить изображения на жесткий диск, как только я их покажу. Я попытался использовать основной поток, но все замедлилось слишком сильно. Затем я попытался использовать ThreadPool (см. Код ниже). Это не замедляет отображение, но я обнаружил, что изображения не сохраняются должным образом. Похоже, что они не в ожидаемом порядке, и после сохранения примерно 50 изображений последующие данные изображения выглядят искаженными. Я предполагаю, что слишком много потоков запускается.Как сохранить файлы на жесткий диск в отдельном потоке?

Есть ли лучший способ сделать это? Кажется, мне нужен только один поток, чтобы сохранить изображения. Может быть, какая-то очередь, которая сохраняет каждое изображение последовательно. До тех пор, пока это делается в фоновом режиме и не замедляет отображение. Если кто-то может опубликовать фрагмент кода, который был бы фантастическим.

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

короткий [] изображение1 = новый короткий [20000]; Я уверен, что вы уверены, что ваш образ не займет больше 40000 байт. –

+0

Выполняет ли 'ReadImageFromCamera 'копирование данных в массив, который вы передаете, или же он создает новый массив, который присваивается' image1'? –

+0

да простите, что только для иллюстрации. Изображения различаются по размеру в зависимости от пользовательских настроек (но не сразу после начала потоковой передачи). Я читаю все данные изображения ОК, поэтому проблем нет. – Doug

ответ

0

При обращении к нитям заказ больше не находится под вашим контролем. Пул потоков может выбрать планирование потоков в любом порядке, который ему нравится. Если вам нужно, чтобы события происходили последовательно в определенном порядке, потоковая передача в любом случае не имеет большого смысла.

Что касается поврежденных изображений, похоже, что экземпляр short[] image1 передается. Неясно, что происходит внутри ReadImageFromCamera, но поскольку вы передаете в него предварительно инициализированный массив, возможно, что метод будет использовать этот массив и просто скопировать данные в него (хотя ключевое слово ref указывает, что он может создать совершенно новый массив экземпляр и назначьте это вместо этого). Затем вы передаете этот массив экземпляру в WriteImageToFile в отдельном потоке.

Между тем, в параллельном режиме вы получите следующее изображение. Теперь у вас есть сценарий, в котором ReadImageFromCamera может записывать данные в массив одновременно с тем, как WriteImageToFile хранит данные на диске. Там у вас есть испорченный образ. Этого можно избежать, передавая новый экземпляр массива в 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); 

Тем не менее, as has been mentioned by Al Kepp, так как у вас есть только один жесткий диск, запуск большого количества потоков не может быть лучшим вариантом здесь. Вы могли бы заглянуть в один длинный отдельный поток для хранения данных на диске и поместить изображения в какую-то очередь, чтобы поток хранилища собирал данные и записывал их на диск. Это связано с собственным набором проблем, связанных с параллелизмом, ограничивая размер очереди, а что нет.

+0

brlliant за это. – Doug

+0

Я бы предпочел создать новый короткий [] массив вместо вызова Clone(). Я хочу создать новый короткий [20000] прямо перед вызовом ReadImageFromCamera(). Не является ли Clone() излишне медленным для этого сценария? Зачем нам нужно делать клон, когда мы собираемся выбросить оригинал сразу после этого. –

6

Я думаю, вам не следует начинать новую тему для каждого конкретного изображения. Поскольку у вас есть только один жесткий диск и хранить все файлы в одном каталоге, вы должны использовать только один поток записи на диск. Затем я бы рекомендовал использовать некоторую параллельную очередь для переноса заданий из потока камеры в поток записи на диск. Я не показываю «фрагмент кода», потому что это не то, что вы можете написать в хорошем качестве в нескольких строках кода.

Также вы обязательно должны помещать «новый короткий [20000]» для каждого изображения, иначе оно будет перезаписано следующим изображением, прежде чем вы сохраните его на диск.

Кроме того, я ожидал бы, что достаточно писать файлы в основном потоке, потому что при использовании данных на диске Windows автоматически использует параллельные методы (главным образом дисковый кэш). Вы уверены, что ваше оборудование достаточно быстро, чтобы записывать все эти данные в реальном времени?

+0

Я пробовал записывать данные на диск в основной поток, но заметно замедлил отображение. Резьбовое решение не замедляет отображение – Doug

0

Вам необходимо создать отдельный буфер для потока для чтения данных, иначе основной поток перезапишет его, когда вы выгрузите его в файл. То, как вы это делаете, похоже, копирует только ссылки (image1 в частности).

Итак:

ThreadPool.QueueUserWorkItem(WriteImageToFile, data); 

вместо данных, которые вы будете посылать в глубокой копии data. Так как кажется, что вы уже это делаете, но в рабочем потоке вам просто нужно переместить копию до отправки.

НТН

+2

Неглубокой копии будет достаточно. Поскольку элементы имеют неизменяемый тип значения, нет способа изменить их таким образом, чтобы скомпрометировать ссылку клонированного массива. –

+0

@Fredrik: Неглубокой копии достаточно. +1 Но причина не в неизменности элементов, потому что массив в целом не является неизменным. В этом сценарии просто нетронутый - он отображается и сохраняется на диске, поэтому нет причин делать глубокую копию. –

+0

@Fredrik, если достаточно мелкой копии, почему вы клонируете ее в свой ответ? – Simone

0

Вы должны проверить, прежде чем думать о потоках, если скорость нормального диска будет достаточно для вашей задачи, как вы можете создавать образы быстрее, чем запись на диск. Если создание изображения быстрее, чем запись, я бы посмотрел на использование диска с памятью, но тогда вам нужно рассчитать, достаточно ли этого размера, пока вы не остановите камеру, чтобы вы могли записать на обычный диск в одночасье.
Если вы используете .NET 4.0, я бы предположил, что вы используете Concurrent queue вместе с нормальным потоком (по мере того, как поток будет работать до завершения программы).

0

Вы можете сделать следующее.

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 }); 
     } 
    } 

Позже вы можете использовать его как.

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

Быстрый и грязный путь начинается одной новой нити и работать с глобальными участниками класса - новый поток должен иметь возможность доступа к ним в то время как основной поток будет обновлять их.

Прежде всего, эти линии вне любой функции:

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

Теперь измените код в «основной» нить к этому:

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; 

И, наконец, имеют такую ​​функцию для новая резьба:

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

Я не могу поверить, насколько хорош этот сайт. Его фантастика, спасибо за все ответы. – Doug

+0

Я не могу поверить, насколько хорош этот сайт. Его фантастика, спасибо за все ответы. Ответ Фредерика Морка, кажется, все решил. data.image = (short []) image1.Clone(); – Doug

+0

Shadow Wizard Я собираюсь попробовать свой код, так как Im все еще немного обеспокоен QueueUserWorkItem будет использовать слишком много потоков в какой-то момент. – Doug

Смежные вопросы