Read an image file asynchronously.

Hi all,

I would like to read an image file asynchronously and Im trying to achieve that using async/await from C#. However, I still notice a significant lag with this implementation. Using the profiler I'm pretty confident it is it the FileStream.ReadAsync() method that takes ages to complete and causes the game from updating.

Here's some code.

// Static class for reading the image.
class AsyncImageReader
{
    public static async Task<byte[]> ReadImageAsync(string filePath)
        {
            byte[] imageBytes;
            didFinishReading = false;
            using (FileStream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, true))
            {
                imageBytes = new byte[fileStream.Length];
                await fileStream.ReadAsync(imageBytes, 0, (int) fileStream.Length);

            }

            didFinishReading = true;

            return imageBytes;
        }
 }

    // Calling function that is called by the OnCapturedPhotoToDisk event.
    public void AddImage(string filePath)
    {
        Task<byte[]> readImageTask = AsyncImageReader.ReadImageAsync(filePath);
        byte [] imageBytes = await readImageTask;
    }

Best Answer

  • Accepted Answer

    For anyone else who stumbled through this dangerous sludge of confusion here's what I ended up with. Not perfect, since I still notice a tracking loss but better than nothing. In the calling function don't use the await keyword, just fire and forget. Let the callback do the work that depends on the read file. This works with any other file type too, I think/hope.

    public static async Task ReadImageBytesAsync(string filepath, Action<byte[]> callback)
    {
        didFinishReading = false;
        byte[] imageBytes;
        using (FileStream sourceStream = new FileStream(filepath, FileMode.Open, FileAccess.Read, FileShare.None, bufferSize, FileOptions.Asynchronous))
        {
            imageBytes = new byte[sourceStream.Length];
            await sourceStream.ReadAsync(imageBytes, 0, (int) sourceStream.Length);
        }
    
        didFinishReading = true;
    
        callback(imageBytes);
    
    }
    

Answers

  • First let me ask where is this code running? Unity or a C# directX app? If it's Unity, Unity basically requires all reference classes to be accessed from the main UI thread. Value types can be accessed from background threads. What this means is your app will basically wait regardless of your async/await patterns. You'll need to implement a dispatcher pattern in your Unity class. It if it's C# DX then another note, have you tried chunking your reads? Read in chunks at a time, not the whole file all at once?

    Just some thoughts to help you out.

    Dwight Goins
    CAO & Founder| Independent Architect | Trainer and Consultant | Sr. Enterprise Architect
    MVP | MCT | MCSD | MCPD | SharePoint TS | MS Virtual TS |Windows 8 App Store Developer | Linux Gentoo Geek | Raspberry Pi Owner | Micro .Net Developer | Kinect For Windows Device Developer
    http://dgoins.wordpress.com

  • @Dwight_Goins_EE_MVP Thanks. Yes it is in Unity. I will have to look up dispatcher pattern to implement this.

    As for anyone else who might come here looking for a quick fix, what I did what hide all the holograms while it's reading so you don't notice the tracking loss/no rendering during that time.

  • Dwight Goins
    CAO & Founder| Independent Architect | Trainer and Consultant | Sr. Enterprise Architect
    MVP | MCT | MCSD | MCPD | SharePoint TS | MS Virtual TS |Windows 8 App Store Developer | Linux Gentoo Geek | Raspberry Pi Owner | Micro .Net Developer | Kinect For Windows Device Developer
    http://dgoins.wordpress.com

  • Accepted Answer

    For anyone else who stumbled through this dangerous sludge of confusion here's what I ended up with. Not perfect, since I still notice a tracking loss but better than nothing. In the calling function don't use the await keyword, just fire and forget. Let the callback do the work that depends on the read file. This works with any other file type too, I think/hope.

    public static async Task ReadImageBytesAsync(string filepath, Action<byte[]> callback)
    {
        didFinishReading = false;
        byte[] imageBytes;
        using (FileStream sourceStream = new FileStream(filepath, FileMode.Open, FileAccess.Read, FileShare.None, bufferSize, FileOptions.Asynchronous))
        {
            imageBytes = new byte[sourceStream.Length];
            await sourceStream.ReadAsync(imageBytes, 0, (int) sourceStream.Length);
        }
    
        didFinishReading = true;
    
        callback(imageBytes);
    
    }
    
Sign In or Register to comment.