Как предварительно загрузить изображения в фоновом потоке? - PullRequest
1 голос
/ 05 февраля 2010

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

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

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

Ответы [ 4 ]

2 голосов
/ 05 февраля 2010

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

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 голосов
/ 04 марта 2011

Я смотрел на это вчера и не мог найти много по этому вопросу. Там на самом деле довольно простое решение проблемы. Используйте WebClient для асинхронной загрузки изображений в поток, а затем добавьте этот поток в BitmapImage. Ниже приведен пример того, как я реализовал предварительную загрузку списка изображений. в примере используется библиотека Reactive Extensions (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;
    });
}
0 голосов
/ 05 февраля 2010

Марсель,

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

0 голосов
/ 05 февраля 2010

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

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

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

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

Ник

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...