//Listing 12.9 Producer/consumer using TPL Dataflow public static async Task CompressAndEncrypt( Stream streamSource, Stream streamDestination, int maxDegreeOfParallelism = 4, long chunkSize = 1048576, CancellationTokenSource cts = null) { cts = cts ?? new CancellationTokenSource(); //#A var compressorOptions = new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = maxDegreeOfParallelism, BoundedCapacity = 20, CancellationToken = cts.Token }; //#B var inputBuffer = new BufferBlock <CompressingDetails>( new DataflowBlockOptions { CancellationToken = cts.Token, BoundedCapacity = 20 }); //#B var compressor = new TransformBlock <CompressingDetails, CompressedDetails>( async details => { var compressedData = await IOUtils.Compress(details.Bytes); //#C return(details.ToCompressedDetails(compressedData)); //#D }, compressorOptions); var encryptor = new TransformBlock <CompressedDetails, EncryptDetails>( async details => { byte[] data = IOUtils.CombineByteArrays(details.CompressedDataSize, details.ChunkSize, details.Bytes); //#E var encryptedData = await IOUtils.Encrypt(data); //#F return(details.ToEncryptDetails(encryptedData)); //#D }, compressorOptions); var asOrderedAgent = Agent.Start((new Dictionary <int, EncryptDetails>(), 0), async((Dictionary <int, EncryptDetails>, int)state, EncryptDetails msg) => { //#G (Dictionary <int, EncryptDetails> details, int lastIndexProc) = state; details.Add(msg.Sequence, msg); while (details.ContainsKey(lastIndexProc + 1)) { msg = details[lastIndexProc + 1]; await streamDestination.WriteAsync(msg.EncryptedDataSize, 0, msg.EncryptedDataSize.Length); await streamDestination.WriteAsync(msg.Bytes, 0, msg.Bytes.Length); //#H lastIndexProc = msg.Sequence; details.Remove(lastIndexProc); //#I } return(details, lastIndexProc); }, cts);