Case study: CopyToAsync and copytoasync

Source: Internet
Author: User

Case study: CopyToAsync and copytoasync
Return to the Task-based asynchronous mode in this series of directories-Overview

 

It is a useful and common operation to copy a stream to another stream. The Stream. CopyTo method has been added to. Net 4 to meet this requirement. For example, download data at a specified URL:

public static byte[] DownloadData(string url){    using(var request = WebRequest.Create(url))    using(var response = request.GetResponse())    using(var responseStream = response.GetResponseStream())    using(var result = new MemoryStream())    {        responseStream.CopyTo(result);        return result.ToArray();    }}

To improve the response capability and scalability, we want to use the TAP-based mode to implement the above functions. You can try to do the following:

public static async Task<byte[]> DownloadDataAsync(string url){    using(var request = WebRequest.Create(url))    {        return await Task.Run(() =>        {            using(var response = request.GetResponse())            using(var responseStream = response.GetResponseStream())            using(var result = new MemoryStream())            {                responseStream.CopyTo(result);                return result.ToArray();            }        }    }}

 

This implementation improves the response capability if used by the UI thread because it is out of the call thread for downloading data tasks from the network stream and copies the network stream to finally convert the downloaded data into an array of memory streams. However, this implementation has no effect on scalability because it still executes synchronous I/O and blocks thread pool threads while waiting for data download. Instead, we want the following functional code:

public static async Task<byte[]> DownloadDataAsync(string url){    using(var request = WebRequest.Create(url))    using(var response = await request.GetResponseAsync())    using(var responseStream = response.GetResponseStream())    using(var result = new MemoryStream())    {        await responseStream.CopyToAsync(result);        return result.ToArray();    }}

Unfortunately, the asynchronous CopyToAsync method is missing in. Net 4, and only the Stream class has a synchronous CopyTo method. Now we provide an implementation:

public static void CopyTo(this Stream source, Stream destination){    var buffer = new byte[0x1000];    int bytesRead;    while((bytesRead = source.Read(buffer, 0, buffer.Length)) > 0)    {        destination.Write(buffer, 0, bytesRead);    }}

To provide an asynchronous CopyTo implementation, we can use the compiler to implement the TAP capability and slightly modify this implementation:

public static async Task CopyToAsync(this Stream source, Stream destination){    var buffer = new byte[0x1000];    int bytesRead;    while((bytesRead = await source.ReadAsync(buffer, 0, buffer.Length)) > 0)    {        await destination.WriteAsync(buffer, 0, bytesRead);    }}

Here we change the return type from void to Task, and replace Read and Write with ReadAsync and WriteAsync respectively, the prefix of the await keyword related to the context is added before the call of ReadAsync and WriteAsync .. Net 4 does not contain ReadAsycn and WriteAsync, but it can be implemented based on Task. Factory. FromAsync. This description is described in the "Tasks and APM" section in the previous article:

public static Task<int> ReadAsync(    this Stream source, byte [] buffer, int offset, int count){    return Task<int>.Factory.FromAsync(source.BeginRead, source.EndRead,         buffer, offset, count, null);}public static Task WriteAsync(    this Stream destination, byte [] buffer, int offset, int count){    return Task.Factory.FromAsync(        destination.BeginWrite, destination.EndWrite,         buffer, offset, count, null);}

With these methods, we can successfully implement the CopyToAsync method. We can also add a CancellationToken to the method to support Revocation requests. This CancellationToken will be monitored after each read/write during the replication process (if ReadAsync and WriteAsync support revocation, you can also thread CancellationToken to those calls ):

public static async Task CopyToAsync(    this Stream source, Stream destination,     CancellationToken cancellationToken){    var buffer = new byte[0x1000];    int bytesRead;    while((bytesRead = await source.ReadAsync(buffer, 0, buffer.Length)) > 0)    {        await destination.WriteAsync(buffer, 0, bytesRead);        cancellationToken.ThrowIfCancellationRequested();    }}

[Note: this type of revocation is also useful in synchronous CopyTo implementation. The passed CancellationToken enables revocation. The implementation depends on an object that can be canceled returned from the method, but it is too late for the implementation to receive the object because there is no cancellation left when the synchronous call is complete .]

We also added support for progress notifications, including how much data has been copied so far:

public static async Task CopyToAsync(    this Stream source, Stream destination,     CancellationToken cancellationToken,    IProgress<long> progress){    var buffer = new byte[0x1000];    int bytesRead;    long totalRead = 0;    while((bytesRead = await source.ReadAsync(buffer, 0, buffer.Length)) > 0)    {        await destination.WriteAsync(buffer, 0, bytesRead);        cancellationToken.ThrowIfCancellationRequested();        totalRead += bytesRead;        progress.Report(totalRead);    }}

With this method, we can now fully implement our DownloadDataAsync method, including adding undo and progress support:

public static async Task<byte[]> DownloadDataAsync(    string url,    CancellationToken cancellationToken,    IProgress<long> progress){    using(var request = WebRequest.Create(url))    using(var response = await request.GetResponseAsync())    using(var responseStream = response.GetResponseStream())    using(var result = new MemoryStream())    {        await responseStream.CopyToAsync(            result, cancellationToken, progress);        return result.ToArray();    }}

It is also possible to further optimize our CopyToAsync method. For example, if we want to use two buffers instead of one, we can read the data read before reading and removing a piece of data, therefore, if both read and write operations use asynchronous I/O, cross latency will occur:

public static async Task CopyToAsync(this Stream source, Stream destination){    int i = 0;    var buffers = new [] { new byte[0x1000], new byte[0x1000] };    Task writeTask = null;    while(true)    {        var readTask = source.ReadAsync(buffers[i], 0, buffers[i].Length))>0;        if (writeTask != null) await Task.WhenAll(readTask, writeTask);        int bytesRead = await readTask;        if (bytesRead == 0) break;        writeTask = destination.WriteAsync(buffers[i], 0, bytesRead);        i ^= 1; // swap buffers    }}

Eliminating unnecessary context conversions is another optimization. As mentioned earlier, when a Task in await is executed by default, it will be transmitted back to the current SynchronizationContext. In the case of CopyToAsynch implementation, it is unnecessary to use such a conversion, because we have not operated any UI status. We can use the advantage class of Task. ConfigureAwait to disable this automatic conversion. To simplify the process, the original asynchronous implementation is modified as follows:

public static Task CopyToAsync(this Stream source, Stream destination){    var buffer = new byte[0x1000];    int bytesRead;    while((bytesRead = await        source.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false)) > 0)    {        await destination.WriteAsync(buffer, 0, bytesRead)            .ConfigureAwait(false);    }}                                                                                           
Return to the Task-based asynchronous mode in this series of directories-Overview

The series has finally ended. Thank you for your support. Continue to follow our blog and I will produce series of articles.

Author: tkb to simple source: http://www.cnblogs.com/farb/

QQ: 782762625

You are welcome to discuss more!

The copyright of this article is shared by the author and the blog Park. You are welcome to reprint it. Without the consent of the author, the original article link and author must be clearly marked on the article page; otherwise, the legal liability is reserved.
If you think this article is good or rewarding, click[Recommended]Button, because your support is the greatest motivation for me to continue writing and share!

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.