我有一个自定义列表控件,可以显示包含图像缩略图的项目。每个列表项都给出了文件的完整路径,并使用FileStream.BeginRead异步读取它,并且在文件读取完成时需要使列表控件无效。
在任何时候,列表也可以清除项目并重新填充不同的项目。这会调用需要优雅处理文件流的每个项目的Dispose(它仍然可以处于异步读取的中间)。
我将展示我正在使用的代码。我不确定在这样的情况下调用和锁定对象的正确用法是异步加载新文件的请求可能会在另一个文件异步加载的过程中出现。
public string FileName { get; set; }
public Image Image { get; set; }
public Control Parent { get; set; }
private FileStream currentFileStream;
private byte[] buffer;
private object locker = new object();
private bool loading;
private bool disposed;
public void LoadImage(string fileName)
{
FileName = fileName;
lock (locker)
{
currentFileStream = new FileStream(FileName, FileMode.Open, FileAccess.Read, FileShare.Read);
buffer = new byte[currentFileStream.Length];
currentFileStream.BeginRead(buffer, 0, buffer.Length, FileReadComplete, currentFileStream);
loading = true;
}
}
private void FileReadComplete(IAsyncResult ar)
{
FileStream fileStreamComplete = (FileStream)ar.AsyncState;
lock (locker)
{
fileStreamComplete.EndRead(ar);
// If the finished FileStream is the more recent one requested
// And this item has not been disposed
if (fileStreamComplete == currentFileStream && !disposed)
{
try
{
loading = false;
Image = new Bitmap(currentFileStream);
currentFileStream.Close();
currentFileStream.Dispose();
Parent.Invalidate();
}
catch (Exception e)
{
}
finally
{
currentFileStream = null;
}
}
else
{
fileStreamComplete.Close();
fileStreamComplete.Dispose();
}
}
}
protected override void Dispose(bool disposing)
{
lock (locker)
{
base.Dispose(disposing);
if (!disposed)
{
if (disposing)
{
if (Image != null)
Image.Dispose();
}
disposed = true;
}
}
}
修改: 从Dispose()方法中删除了currentFileStream的处理。
编辑#2 : 从LoadImage()函数中删除了currentFileStream的处理。它可能不应该存在,因为文件读取可能正在进行中并且在操作期间无法关闭。无论何时调用FileReadComplete回调,它都会被处理掉。
答案 0 :(得分:1)
我不确定我是否遵循了您的代码,但这是我建议的。
如果您计划将其用于异步只读,请使用其他FileStream构造函数。它可能与您的问题无关,但只是一种更好的方法
currentFileStream = new FileStream(FileName, FileMode.Open, FileAccess.Read, FileShare.Read, 1024 * 8, true);
您是否有任何理由不想提供Bitmap构造函数(或Image.FromFile)的路径并让它加载文件?请记住,在将大量文件加载到内存中时,按顺序加载它们可能会更快(如果文件驻留在顺序访问技术(如硬盘)上)
假设您仍然想要异步加载它,我只是将该功能封装在一个“neatness”的类中
您似乎从同一个流中加载了您已经读入缓冲区的图像。我确信这是一个问题。以下是我对你的代码的改编。主要变化是
我没有尝试过代码,所以你必须告诉我它是否有效
class ImageLoader : IDisposable {
public string FileName { get; set; }
public Image Image { get; set; }
public Control Parent { get; set; }
private FileStream currentFileStream;
private byte[] buffer;
private object locker = new object();
Control parent;
private volatile bool dispose = false;
public ImageLoader(Control parent, string fileName) {
Parent = parent;
FileName = fileName;
Image = null;
currentFileStream = new FileStream(FileName, FileMode.Open, FileAccess.Read, FileShare.Read, 1024 * 8, true);
buffer = new byte[currentFileStream.Length];
currentFileStream.BeginRead(buffer, 0, buffer.Length, new AsyncCallback(FileReadComplete), null);
}
private void FileReadComplete(IAsyncResult ar) {
lock (locker) {
try { currentFileStream.EndRead(ar); } catch (ObjectDisposedException) { }
if (!dispose) {
using (MemoryStream ms = new MemoryStream(buffer))
Image = new Bitmap(ms);
Parent.Invalidate();
}
try { currentFileStream.Close(); } catch(IOException) { }
}
}
public void Dispose() {
lock (locker) {
if (dispose)
return;
dispose = true;
try { currentFileStream.Close(); } catch(IOException) { }
if (Image != null)
Image.Dispose();
}
}
}
EDIT1: 根据你的评论,我在这里添加了回复,因为系统不允许我在那里添加更多文本