Here's how I would go about it (mostly from a readability standpoint rather than a performance one - though the use of immutable data may help contribute to well-measurabl performance and memory usage metrics):

    namespace MD5Library
    {
        using System;
        using System.Collections.Concurrent;
        using System.Collections.Generic;
        using System.ComponentModel;
        using System.IO;
        using System.Linq;
        using System.Security.Cryptography;
        using System.Threading;
        using System.Threading.Tasks;
    
        public interface IFileBlock
        {
            byte[] Data
            {
                get;
            }
    
            int Length
            {
                get;
    
                set;
            }
        }
    
        public sealed class FileBlock : IFileBlock
        {
            private readonly byte[] data;
    
            public FileBlock(int inBufferSize)
            {
                this.data = new byte[inBufferSize];
            }
    
            public byte[] Data
            {
                get
                {
                    return this.data;
                }
            }
    
            public int Length
            {
                get;
    
                set;
            }
        }
    
        public class MD5Hasher
        {
            private readonly int bufferSize = 1024 * 1024 * 16; // 16mb at a time.
    
            public MD5Hasher()
            {
            }
    
            public MD5Hasher(int bufferSize)
            {
                this.bufferSize = bufferSize;
            }
    
            protected int BufferSize
            {
                get
                {
                    return this.bufferSize;
                }
            }
    
            public Task<string> Go(string inFile, CancellationToken ct, IProgress<IProgressReport> prg)
            {
                // Queue with capacity 3x the buffer.
                var fi = new FileInfo(inFile);
                var bigBuffer = new BlockingCollection<IFileBlock>(3);
                var readFileTask = new Task(() => this.ReadFiles(fi, ct, bigBuffer));
                var computeHashTask = new Task<string>(() => this.CalcHash(fi, bigBuffer, ct, prg));
    
                readFileTask.Start();
                computeHashTask.Start();
                return computeHashTask;
            }
    
            private static string HashToString(IEnumerable<byte> inHash)
            {
                return inHash.Aggregate(string.Empty, (current, b) => current + b.ToString("x2"));
            }
    
            private async void ReadFiles(FileInfo fi, CancellationToken ct, BlockingCollection<IFileBlock> bigBuffer)
            {
                using (var stream = File.OpenRead(fi.FullName))
                {
                    int bytesRead;
    
                    do
                    {
                        var fileBlock = new FileBlock(this.bufferSize);
    
                        fileBlock.Length = await stream.ReadAsync(fileBlock.Data, 0, this.bufferSize);
                        bytesRead = fileBlock.Length;
    
                        // Infinite timeout.
                        bigBuffer.TryAdd(fileBlock, -1, ct);
                    }
                    while (bytesRead != 0);
    
                    bigBuffer.CompleteAdding();
                }
            }
    
            private string CalcHash(FileInfo fi, BlockingCollection<IFileBlock> bigBuffer, CancellationToken ct, IProgress<IProgressReport> prg)
            {
                var fileSize = fi.Length;
                var bytesProcessed = 0L;
    
                using (HashAlgorithm hashAlgorithm = MD5.Create())
                {
                    foreach (var b in bigBuffer.GetConsumingEnumerable(ct))
                    {
                        hashAlgorithm.TransformBlock(b.Data, 0, b.Length, b.Data, 0);
                        bytesProcessed += b.Length;
    
                        // Report progress.
                        prg.Report(new ProgressReport(bytesProcessed, fileSize));
                    }
    
                    hashAlgorithm.TransformFinalBlock(new byte[0], 0, 0);
                    return HashToString(hashAlgorithm.Hash);
                }
            }
        }
    
        public interface IProgressReport
        {
            int PercentDone
            {
                get;
            }
    
            string InfoText
            {
                get;
            }
        }
    
        public sealed class ProgressReport : IProgressReport
        {
            private const string PercentComplete = "% complete.";
    
            private readonly int percentDone;
    
            private readonly string infoText;
    
            public ProgressReport(long progress, long total)
            {
                this.percentDone = Convert.ToInt32((100 * progress) / total);
                this.infoText = this.PercentDone + PercentComplete;
            }
    
            public int PercentDone
            {
                get
                {
                    return this.percentDone;
                }
            }
    
            public string InfoText
            {
                get
                {
                    return this.infoText;
                }
            }
        }
    
        // Borrowed from Stephen Cleary: http://nitoprograms.blogspot.co.uk/2012/02/reporting-progress-from-async-tasks.html
        public class PropertyProgress<T> : IProgress<T>, INotifyPropertyChanged
        {
            private readonly SynchronizationContext context;
    
            private T progress;
    
            public PropertyProgress(T initialProgress = default(T))
            {
                this.context = SynchronizationContext.Current ?? new SynchronizationContext();
                this.progress = initialProgress;
            }
    
            public event PropertyChangedEventHandler PropertyChanged;
    
            public T Progress
            {
                get
                {
                    return this.progress;
                }
    
                private set
                {
                    this.progress = value;
                    if (this.PropertyChanged != null)
                    {
                        this.PropertyChanged(this, new PropertyChangedEventArgs("Progress"));
                    }
                }
            }
    
            void IProgress<T>.Report(T value)
            {
                this.context.Post(_ => { this.Progress = value; }, null);
            }
        }
    }