2010-02-05 4 views
1

В моем приложении WPF мне нужно загрузить некоторые изображения. Мне нужно отображать только одно изображение за раз. Если я загружаю изображение, когда это необходимо, происходит небольшая задержка. Поэтому я подумал: «Эй, почему бы не сделать предварительную загрузку в фоновом потоке? Не может быть так сложно». У меня есть опыт работы с потоками, но недостаточно, чтобы знать, что эта мысль была неправильной. Я начал программировать и сталкивался с некоторыми проблемами. Я исправил некоторые проблемы, и я, вероятно, мог бы исправить и другие проблемы, но это привело бы к коду спагетти. Итак, я думаю, что начинать с нуля было бы лучше. Какое начальное строгание необходимо для создания красивой и маленькой загрузочной нити? Есть ли образец или что-то в этом роде?как предварительно загрузить изображения в фоновом режиме?

Вот моя текущая настройка:

  • LinkedList<string> в магазинах трактов к картинам и перейти к следующему изображению
  • Dictionary<string, BitmapImage> для хранения предварительно загруженного изображения

ответ

2

Я хотел бы использовать что-то вроде этого:

class ImageManager 
{ 
    private Dictionary<string, Image> images= 
    new Dictionary<string,Image>(); 

    public Image get(string s) { // blocking call, returns the image 
    return load(s); 
    } 

    private Image load(string s) { // internal, thread-safe helper 
    lock(images) { 
     if(!images.ContainsKey(s)) { 
     Image img=// load the image s 
     images.Add(s,img); 
     return img; 
     } 
     return images[s]; 
    } 
    } 

    public void preload(params string[] imgs) { // non-blocking preloading call 
    foreach(string img in imgs) { 
     BackgroundWorker bw=new BackgroundWorker(); 
     bw.DoWork+=(s,e)=>{ load(img); } // discard the actual image return 
     bw.RunWorkerAsync(); 
    } 
    } 
} 

// in your main function 
{ 
    ImageManager im=new ImageManager(); 
    im.preload("path1", "path2", "path3", "path4"); // non-blocking call 

    // then you just request images based on their path 
    // they'll become available as they are loaded 
    // or if you request an image before it's queued to be loaded asynchronously 
    // it will get loaded synchronously instead, thus with priority because it's needed 
} 
+0

Это выглядит действительно хорошо, но все же это не мешает ждать. Рассмотрим такой пример: у нас есть 100 изображений, доступных для загрузки, например. FTP. Если пользователь нажимает последнее изображение, он будет вынужден дождаться загрузки всех 99 предыдущих изображений. В этом случае это решение имеет только один pro: он не блокирует пользовательский интерфейс. Марсель, пожалуйста, предоставьте нам дополнительную информацию :) –

+0

Если вы нажмете 100-е изображение, оно будет загружено в поток ui по запросу независимо от поставленных в очередь изображений, поэтому оно будет показано как можно скорее, даже если предварительный загрузчик еще не дошел. – Blindy

+0

Итак, в чем смысл использовать такой предварительный загрузчик? :) –

0

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

Однако на что-то приходит в голову: вам либо придется записывать, какие изображения в настоящее время находятся в процессе загрузки, либо допускать несколько нагрузок одного и того же изображения.

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

Итак, должна быть какая-то синхронизация, но она не должна быть слишком сложной.

Ник

+0

Просто синхронизации было недостаточно. Я столкнулся с InvalidOperationExceptions, потому что коллекция принадлежит фоновому потоку. –

+0

Коллекции, такие как LinkedList и Dictionary, не принадлежат «никому».Что бросило InvalidOperationException? –

+0

Если такая коллекция привязана к пользовательскому интерфейсу, она фактически принадлежит потоку пользовательского интерфейса. –

0

Марсель,

WPF предоставляет нам уже с большими механизмами BackgroundWorker и Dispatcher, чтобы вы забыли о написании собственных механизмов резьбы. Однако ваша проблема, похоже, не настолько очевидна для меня. Сколько изображений вам нужно/откуда вы их получите? Пожалуйста, дайте нам больше информации.

+0

В основном это должно быть не более 40-50 фотографий, но я бы хотел, чтобы их можно было просмотреть, если их больше. Изображения поступают из папки на жестком диске. –

+0

Хм, если изображения поступают с HDD, действительно не должно быть никаких далайлов. Однако, если эти изображения довольно большие, вам может понадобиться подготовить меньшие большие пальцы и отобразить их вместо этого, и после показа небольшого большого пальца вы можете запустить фонового работника, чтобы получить исходное изображение (чтобы быть готовым, этот пользователь хочет увеличить большой палец). Что ты говоришь? –

0

Я смотрел в эту вчера и не мог найти много на эту тему. На самом деле это довольно простое решение проблемы. Используйте WebClient для загрузки изображений асинхронно в поток, а затем добавьте этот поток в BitmapImage. Ниже приведен пример того, как я выполнил предварительную загрузку списка изображений. в примере используется Reactive Extensions Library (Rx), но его можно легко реализовать с использованием не Rx (Rx делает код намного более кратким и скрывает много состояний).

public IEnumerable<BitmapImage> BitmapImages { get; private set } 

private void PreloadImages(IEnumerbale<Uri> uriCollection) 
{ 
    var bitmapImages= new List<BitmapImage>(); 

    uriCollection.ToObservable() 
     .SelectMany(LoadImageAsync) 
     .Catch(Observable.Empty<BitmapImage>()) 
     .Subscribe(bitmapImages.Add, 
     () => 
     { 
      BitmapImages = bitmapImages; 
     }); 
} 

private IObservable<BitmapImage> LoadImageAsync(Uri uri) 
{ 
    return Observable.CreateWithDisposable<BitmapImage>(observer => 
    { 
     var downloader = new WebClient(); 
     downloader.OpenReadCompleted += (s, e) => 
     { 
      if (e.Error != null) 
      { 
       observer.OnError(e.Error); 
      } 

      var bitmapImage = new BitmapImage(); 
      bitmapImage.BeginInit(); 
      bitmapImage.StreamSource = e.Result; 
      bitmapImage.EndInit(); 

      observer.OnNext(bitmapImage); 
      observer.OnCompleted(); 
     }; 
     downloader.OpenReadAsync(uri); 

     return downloader; 
    }); 
} 
Смежные вопросы