I'm not sure I followed your code, but this is what I would recommend.
Use a different FileStream constructor if you plan to use it as read-only async. However, this may not be related to your problem, but this is the best way to do this.
currentFileStream = new FileStream(FileName, FileMode.Open, FileAccess.Read, FileShare.Read, 1024 * 8, true);
Any reason you don't want to specify the path to the Bitmap constructor (or Image.FromFile) and upload the file? Remember that when loading a large number of files into memory, their sequential download may be faster (in case the files are based on sequential access technology, for example, on a hard disk).
Assuming you want to load it asynchronously anyway, I just encapsulate this functionality in a class for "accuracy"
It seems you are loading your image from the same stream that you already read in your buffer. I am sure this is a problem. Below is my adaptation of your code. Major changes
- I do not use the variable currentFileStream
- dispose variable becomes volatile as it can be accessed from multiple threads
- Calls in FileStream.Dispose are redundant after FileStream.Close, therefore deleted.
I have not tried the code, so you should tell me if it works
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: Based on your comments, I am adding an answer here, as the system does not allow me to add text there
- Encapsulating such functions in a class (set of functions) or function is good consistency, and in many cases such good practice has performance penalties. You should use it depending on your situation.
- I don't think this is how streams work, but in the case of small files, I see how it will be. To explain this, if you specify a buffer size for files, for example, 8K, a file larger than 8K cannot be cached in memory. Thus, the stream cannot read the entire file without real I / O (forget about what Windows does behind the screen for this). In addition, the Bitmap constructor can potentially perform synchronous I / O, and problems can occur when it is opened in asynchronous mode. MSDN clearly states that a thread should only be used in one mode (synchronous or asynchronous) throughout the life cycle of stream objects. I strongly believe that you should close the stream and read from your own buffer that you have already created. I would say that this works for you, this does not mean that it will work in another scenario; you can just be "happy."
- I agree with the volatile keyword, I just practice it this way, because sometimes when changing the code (for example, I get rid of the lock all together) this saves me from my mistakes.
source share