2010-02-05 6 views
1

내 WPF 응용 프로그램에서 일부 이미지를로드해야합니다. 한 번에 하나의 이미지 만 표시하면됩니다. 필요할 때 이미지를로드하면 약간 지연 될 수 있습니다. 그래서 나는 나 자신에게 이렇게 생각했다. "이봐, 배경 스레드에서 미리로드하지 마시오. 그렇게 힘들 수는 없지." 스레드에 대한 경험이 있지만이 생각이 잘못되었음을 알기에는 충분하지 않습니다. 프로그래밍을 시작하고 몇 가지 문제가 발생했습니다. 나는 문제의 일부를 고쳤고 아마도 다른 문제들도 고칠 수 있었지만 그것은 스파게티 코드를 초래할 것이다. 그래서, 처음부터 시작하는 것이 가장 좋을 것이라고 생각합니다. 좋은 작은 미리로드 된 스레드를 작성하려면 어떤 초기 계획이 필요합니까? 패턴이나 그런 것이 있습니까?백그라운드 스레드에서 이미지를 미리로드하는 방법은 무엇입니까?

여기에 내 현재 설정입니다 : 상점

  • LinkedList<string>이 사진을하는 Pathes과 다음 사진
  • 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 개의 이전 이미지가 모두 다운로드 될 때까지 기다려야합니다. 이 시나리오에서이 솔루션에는 단 하나의 장점 만 있습니다. UI를 차단하지 않습니다. Marcel, 자세한 정보를 제공해주세요 :) –

+0

100 번째 이미지를 클릭하면 대기중인 이미지와 관계없이 요청시 ui 스레드에로드되므로 프리 로더가 가능한 빨리 표시됩니다 아직 도달하지 못했습니다. – Blindy

+0

그런 프리 로더를 사용할 때의 요점은 무엇입니까? :) –

0

를 저장 배경 스레드입니다. 또한 작업 단위가 꽤 크기 때문에 컬렉션의 동기화에 너무 많은 경합이 있어서는 안됩니다. 유사한 알고리즘의 예를 찾을 수도 있지만, 자신 만의 구현을해야 할 것 같습니다. 그렇게 복잡한 것은 아닙니다.

하나의 것이 마음에 들지만, 어떤 이미지가 현재로드 중인지 또는 같은 이미지의 여러로드를 허용해야한다는 기록을 유지해야합니다.

예를 들어 UI에 아직로드되지 않은 이미지가 필요한 경우 해당 이미지를 우선 순위로로드하려고 할 수 있습니다. 백그라운드 스레드가 해당 이미지를로드하는 중임을 알게되면이 스레드가 사용 가능할 때까지 기다릴 수 있습니다. UI 스레드에서로드를 수행하기로 결정한 경우 백그라운드 스레드가 이미있는로드 된 이미지를 추가하려고 할 가능성이 있습니다.

그래서 동기화가 필요하지만 너무 복잡해서는 안됩니다.

+0

그냥 동기화가 충분하지 않았습니다. 컬렉션이 백그라운드 스레드에 의해 소유 되었기 때문에 InvalidOperationExceptions를 만났습니다. –

+0

LinkedList 및 Dictionary와 같은 컬렉션은 모든 스레드가 "소유"하지 않습니다.InvalidOperationException을 던진 것은 무엇입니까? –

+0

이러한 콜렉션이 UI에 바인드되면 실제로는 UI 스레드가 소유됩니다. –

0

마르셀,

WPF는 자신의 스레드 메커니즘을 작성 잊을 수 있도록 BackgroundWorker에디스패처의 훌륭한 메커니즘 이미 우리를 제공합니다. 그러나 당신의 문제는 나를 위해 명백한 것 같지 않습니다. 얼마나 많은 이미지가 필요합니까/어디에서 가져 옵니까? 우리에게 더 많은 정보를주세요.

+0

대부분 40 ~ 50 장의 사진이 아니어야합니다. 그러나 더 많은 것이 있으면 나는 prepeared되고 싶습니다. 이미지는 HDD의 폴더에서 가져옵니다. –

+0

흠, 이미지가 HDD에서 나온다면 실제로는 더러움이 없어야합니다. 그러나 이러한 이미지가 상당히 큰 경우 작은 엄지 손가락을 준비하고 대신 표시 할 수 있으며 작은 엄지 손가락이 표시되면 배경 작업자를 시작하여 원본 이미지를 얻을 수 있습니다 (준비하려면 해당 사용자가 무지). 너 무슨 소리 야? –

0

나는이 어제에보고되었다 피사체에 많이 찾을 수 없습니다. 실제로 문제에 대한 간단한 해결책이 있습니다. WebClient을 사용하여 이미지를 비동기 적으로 스트림에로드 한 다음 해당 스트림을 BitmapImage에 추가하십시오. Bellow는 이미지 목록의 사전로드를 구현 한 방법의 예입니다. 이 예에서는 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; 
    }); 
} 
관련 문제