在我的WPF应用程序中,我需要加载一些图像。我只需要一次显示一个图像。如果我在需要时加载图像,则会有一点延迟。所以我心想:“嘿,为什么不在后台线程中做一些预加载?不能那么难。”我对线程有一些经验,但还不足以知道这个想法是错误的。我开始编程并遇到一些问题。我解决了一些问题,我可能也可以解决其他问题,但这会导致意大利面条代码。所以,我认为从头开始将是最好的。需要什么初始规划来构建一个漂亮的小预加载线程?是否有类似的模式?
这是我目前的设置:
LinkedList<string>
将照片存储到照片并导航到下一张照片Dictionary<string, BitmapImage>
存储预加载的图片答案 0 :(得分: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
}
答案 1 :(得分:0)
对于后台线程来说,这听起来确实很好用。此外,由于您的工作单元非常大,因此对集合的同步不应有太多争用。您可能会找到类似算法的示例,但我认为您必须推出自己的实现 - 它并不复杂。
但有一点需要注意:您要么必须记录当前正在加载的图像,要么承受同一图像的多次加载。
例如,如果您的UI需要尚未加载的图像,您可能希望将该图像作为优先级加载。如果您知道后台线程正在加载该图像,您可以等待它变为可用。如果您决定只在UI线程上执行加载,则后台线程可能会尝试添加已存在的已加载图像。
因此,必须有一些同步,但它不应该太复杂。
尼克
答案 2 :(得分:0)
马塞尔,
WPF为我们提供了很好的 BackgroundWorker 和 Dispatcher 机制,让您忘记编写自己的线程机制。但是你的问题对我来说似乎并不那么明显。您需要多少张图片/从哪里获得这些图片?请给我们更多信息。
答案 3 :(得分: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;
});
}