单点线程-请求的顺序/优先级
本文关键字:顺序 优先级 请求 线程 单点 | 更新日期: 2023-09-27 18:20:50
我在使用UICollectionView时遇到了一些性能问题,其中单元格显示图像(从HD加载)。
我通过在后台加载图像来解决这个问题。
基本上,在我的"GetCell"方法中,我会检查图像是否在我的ImageCache中。
- 如果是,请在单元格中的ImageView上设置图像
- 如果没有,请在后台加载图像,并请求重新加载该特定图像项目(我请求重新加载,因为我不知道单元格是否被回收同时,因此直接设置图像是不安全的)
我的后台流程片段:
ThreadPool.QueueUserWorkItem (delegate {
ImagesCache.AddImageToCache(""+indexPath.Row,ImageForPosition(indexPath.Row));
InvokeOnMainThread (delegate {
collectionView.ReloadItems(Converter.ToIndexPathArray(indexPath));
});
});
它工作正常,但如果你快速滚动,它会加载所有这些异步任务,问题是它会按顺序执行请求(FIFO)。因此,当您快速滚动时,不可见单元格的图像将在可见单元格的图片之前加载。
有人知道我如何优化这个过程以获得更好的用户体验吗?因为如果我把它扩展到包括来自互联网的图像,问题会更糟(因为下载)。
增加同时线程的最大数量将允许稍后添加的线程立即启动,但这将降低整体性能/下载速度,因此这也不是一个真正的解决方案。
谢谢,Matt
简而言之,我项目的解决方案是:一个thread
下载支持queue
的图像。再加上在目标UI
控件中的检查,它没有出列以供重用。
长版本:
- 用方法
Start
/Stop
实现queue
。当Start
调用时,启动后台线程,在繁忙循环(while true { DoSomething(); }
)中,后台线程将尝试将请求从队列中出列。如果没有排队,就睡一会儿。如果退出队列,请执行它(下载映像)。Stop
方法应该说线程退出循环:
public void Start()
{
if (started) {
return;
}
started = true;
new Thread (new ThreadStart (() => {
while (started) {
var request = GetRequest();
if (request != null) {
request.State = RequestState.Executing;
Download (request);
} else {
Thread.Sleep (QueueSleepTime);
}
}
})).Start ();
}
public void Stop()
{
started = false;
}
- 然后,在
queue
中制作一个私有的方法来下载图像,该方法的逻辑是:检查文件缓存中的图像。如果文件可用,则读取并返回。如果不可用,则下载该文件,将其保存到文件中,然后返回(调用Action<UIImage> onDownload
)或出错(调用Action<Exception> onError
)。在queue
的忙循环中调用此方法。将其命名为Download
:
public Download(Request request)
{
try {
var image = GetImageFromCache(request.Url);
if (image == null) {
image = DownloadImageFromServer(request.Url); // Could be synchronous
}
request.OnDownload(image);
} catch (Exception e) {
request.OnError(e);
}
}
- 然后,创建一个公共方法,将请求添加到队列中。模式
Command
可用于包装队列的请求:存储Actions
,当前State
。将其命名为DownloadImageAsync
:
public DownloadImageAsync(string imageUrl, Action<UIImage> onDownload, Action<Exception> onError)
{
var request = MakeRequestCommand(imageUrl, onDownload, onError);
queue.Enqueue(request);
}
- 当
UITableViewCell
准备放映并请求下载图像时:
// Custom UITableViewCell method, which is called in `UITableViewSource`'s `GetCell`
public void PrepareToShow()
{
var imageURLClosure = imageURL;
queue.DownloadImageAsync(imageURL, (UIImage image) => {
if (imageURLClosure == imageURL) {
// Cell was not dequeued. URL from request and from cell are equals.
imageView.Image = image;
} else {
// Do nothing. Cell was dequeued, imageURL was changed.
}
}, (Exception e) => {
// Set default image. Log.
});
}
检查(imageURLClose==imageURL)对于避免快速滚动时在一个UIImageView
中显示多个图像至关重要。一个单元格可以初始化多个请求,但只能使用最后一个结果。
进一步改进:
- 后进先出法执行。如果尚未运行任何请求,请添加新的请求开始
- 使用
Action<byte[]> onDownload
代替Action<UIImage> onDownload
实现跨平台代码兼容性 - 当小区变为可见时,可取消
download image
请求(WillMoveToSuperview
)。嗯,这不是很必要。第一次下载后,图像将在缓存中,因此对图像的任何进一步请求都将快速完成。感谢缓存 - 内存缓存中。因此,在最坏的情况下,链将是:
Find image in in-memory cache
->Find image in file cache
->Downloading from server
。