Comprehensive Analysis of asynchronous programming in C #

Source: Internet
Author: User

Comprehensive Analysis of asynchronous programming in C #
When we deal with some long-term calls, it will often lead to problems such as interface stop response or excessive occupation of IIS threads. At this time, we need to use asynchronous programming to solve these problems, however, it is usually easy to say, but it is true that asynchronous programming is a completely different programming idea than Synchronous Programming. for developers who are used to Synchronous Programming, it is more difficult in the development process, and its controllability is not strong. In. NET Framework5.0, Microsoft has provided us with new language features, so that we can use asynchronous programming as similar and simple as using synchronous programming, this article will explain some limitations of the callback-based moral asynchronous programming model in earlier versions of the Framework and the new API if we simply implement the same development task. Why is remote resource programming always confusing? Unlike "local resources", there will always be many unexpected situations in remote resource access, the unstable network environment, machine Server failure, will cause many programmers completely uncontrollable problems, so this requires programmers to need more to protect remote resource calls, manage call cancellation, supermarket, thread waiting, and handling threads that haven't responded for a long time. While in. in NET, we usually ignore these challenges. In fact, we have a variety of unused modes to process asynchronous programming, such as not testing threads when dealing with IO-intensive or high-latency operations, in most cases, we have two methods, synchronous and asynchronous, to do this. However, the problem is that the current mode is very prone to confusion and code errors, or developers will give up and use blocking methods for development. In today's. NET provides a very similar programming experience for Synchronous Programming. Developers do not need to process many situations that only occur in asynchronous programming, asynchronous calls are clear and non-transparent, and easy to combine with Synchronous Code. In the past, we had a bad experience. The best way to understand this problem was our most common situation: the user interface had only one thread, and all the work was running on this thread, the client program cannot respond to the user's mouse time, probably because the application is blocked by a time-consuming operation, this may be because the thread is waiting for a network ID or is doing a CPU-intensive computing. At this time, the user interface cannot get the running time, and the program is always busy. This is a very poor user experience. For many years, the methods to solve this problem are asynchronous calls. Do not wait for a response and return the request as soon as possible so that other events can be executed simultaneously, only when the request has final feedback, the application is notified so that the customer code can execute the specified code. The problem is that the asynchronous code completely destroys the code process. The callback proxy explains how to work later, but how to wait in a while loop? An if statement? A try block or a using block? How to explain "what to do next "? Let's take a look at the following example:

public int SumPageSizes(IList<Uri> uris)        {            int total = 0;            foreach (var uri in uris)            {                txtStatus.Text = string.Format("Found {0} bytes...", total);                var data = new WebClient().DownloadData(uri);                total += data.Length;            }            txtStatus.Text = string.Format("Found {0} bytes total", total);            return total;        }

 

This method downloads files from a uri list, counts their sizes, and updates the status information at the same time. Obviously, this method does not belong to the UI thread because it takes a very long time to complete, in this way, the UI will be completely suspended, but we hope that the UI will be continuously updated. How can this problem be solved? We can create a background program so that it continuously sends data to the UI thread for the UI to update itself. This seems a waste of time, this thread spends most of its time waiting and downloading, but sometimes this is exactly what we need to do. In this example, WebClient provides an asynchronous version of DownloadData method-DownloadDataAsync, which returns immediately and then triggers an event after DownloadDataCompleted, this allows the user to write an asynchronous version of the method to split the tasks to be done, the call returns immediately and completes the subsequent call on the UI thread, so as not to block the UI thread. The following is the first attempt:
public void SumpageSizesAsync(IList<Uri> uris)        {            SumPageSizesAsyncHelper(uris.GetEnumerator(), 0);        }        public void SumPageSizesAsyncHelper(IEnumerator<Uri> enumerator, int total)        {            if (enumerator.MoveNext())            {                txtStatus.Text = string.Format("Found {0} bytes...", total);                var client = new WebClient();                client.DownloadDataCompleted += (sender,e)=>{                    SumPageSizesAsyncHelper(enumerator, total + e.Result.Length);                };                client.DownloadDataAsync(enumerator.Current);            }            else            {                txtStatus.Text = string.Format("Found {0} bytes total", total);            }        } 

 

Then this is still bad. We break down a neat foreach loop and manually obtain an enumerator. Each call creates an Event Callback. The Code replaces the loop with recursion. You should not be able to look directly at this type of code. Don't worry. It's not over yet. The original code returns a total number and displays it. The new version is returned to the caller before the statistics are complete. How can we get a result and return it to the caller? The answer is: the caller must support a callback, which can be called after the statistics are complete. However, what should I do with exceptions? The original code does not focus on exceptions. It will always be passed to the caller. In the asynchronous version, we must expand back to let exceptions spread. When exceptions occur, we have to explicitly let it spread. Eventually, these needs further confuse the Code:
public void SumpageSizesAsync(IList<Uri> uris,Action<int,Exception> callback)        {            SumPageSizesAsyncHelper(uris.GetEnumerator(), 0, callback);        }        public void SumPageSizesAsyncHelper(IEnumerator<Uri> enumerator, int total,Action<int,Exception> callback)        {            try            {                if (enumerator.MoveNext())                {                    txtStatus.Text = string.Format("Found {0} bytes...", total);                    var client = new WebClient();                    client.DownloadDataCompleted += (sender, e) =>                    {                        SumPageSizesAsyncHelper(enumerator, total + e.Result.Length,callback);                    };                    client.DownloadDataAsync(enumerator.Current);                }                else                {                    txtStatus.Text = string.Format("Found {0} bytes total", total);                    enumerator.Dispose();                    callback(total, null);                }            }            catch (Exception ex)            {                enumerator.Dispose();                callback(0, ex);            }                    }

 

When you look at the code again, can you tell clearly what JB is? I'm afraid no. At first we just wanted to replace the blocked call with an asynchronous call as the synchronous method, so that it could be packaged in a foreach loop, think about trying to combine more asynchronous calls or have a more complex control structure. This is not a solution of SubPageSizesAsync scale. Our real problem is that we can no longer explain the logic in these methods, and our code is completely out of band. A lot of work in asynchronous Code makes the whole thing seem unreadable and full of bugs. Now, we have a new function to solve the above problems. The code of the asynchronous version will be as follows:
public async Task<int> SumPageSizesAsync(IList<Uri> uris)        {            int total = 0;            foreach (var uri in uris)            {                txtStatus.Text = string.Format("Found {0} bytes...", total);                var data = await new WebClient().DownloadDataTaskAsync(uri);                total += data.Length;            }            txtStatus.Text = string.Format("Found {0} bytes total", total);            return total;        } 

 

In addition to the highlighted part, the code above is very similar to the code of the synchronization version, the code process has never changed, and we have not seen any callbacks, however, this does not mean that there are actually no callback operations. The compiler will handle these tasks and you do not need to worry about them any more. The Asynchronous Method uses Task <int> to replace the Int type returned in the past. Task and Task <T> are provided in the current framework to represent a running job. There are no additional methods for asynchronous methods. In order to differentiate the methods for Synchronous versions, we add Async after the method name as the new method name. The above method is asynchronous, which means that the method experiences different treatment from the compiler, and some of them will be called back, and the Task <int> is automatically created as the return type. Explanation of this method: Inside the method, call another Asynchronous Method DownloadDataTaskAsync, which quickly returns a variable of the <byte []> type of the Task, it will be activated after the data download is complete. Until now, we don't want to do anything before the data is complete, so we use await to wait for the Operation to complete. It seems that the await keyword blocks the thread until the data downloaded by the task is available. In fact, it indicates the callback of the task and returns immediately. After the task is completed, it executes the callback. The TasksTask and Task <T> types already exist in. in NET Framework 4.0, a Task represents an ongoing activity. It may be a CPU-intensive job or an IO operation running in a separate thread, it is also very easy to manually create a task that does not work in a separate thread:
static async void ReadAssignedFile()        {            byte[] buffer;            try            {                double length = await ReadFileAsync("SomeFileDoNotExisted.txt", out buffer);            }            catch (Exception ex)            {                Console.WriteLine(ex.Message);            }        }        static Task<double> ReadFileAsync(string filePath,out byte[] buffer)         {            Stream stream = File.Open(filePath, FileMode.Open);            buffer = new byte[stream.Length];            var tcs = new TaskCompletionSource<double>();            stream.BeginRead(buffer, 0, buffer.Length, arr =>            {                try                {                    var length = stream.EndRead(arr);                    tcs.SetResult(stream.Length);                }                catch (IOException ex)                {                    tcs.SetException(ex);                }            }, null);            return tcs.Task;        } 

 

Once a TaskCompletionSource object is created, you can return the Task object associated with it. After the related work is completed, the Customer Code will get the final result, in this case, tasks do not occupy their own threads. If the actual Task fails, the Task can carry exceptions from the sample and spread upwards. If await is used, the client code exception is triggered:
static async void ReadAssignedFile()        {            byte[] buffer;            try            {                double length = await ReadFileAsync("SomeFileDoNotExisted.txt", out buffer);            }            catch (Exception ex)            {                Console.WriteLine(ex.Message);            }        }        static Task<double> ReadFileAsync(string filePath,out byte[] buffer)         {            Stream stream = File.Open(filePath, FileMode.Open);            buffer = new byte[stream.Length];            var tcs = new TaskCompletionSource<double>();            stream.BeginRead(buffer, 0, buffer.Length, arr =>            {                try                {                    var length = stream.EndRead(arr);                    tcs.SetResult(stream.Length);                }                catch (IOException ex)                {                    tcs.SetException(ex);                }            }, null);            return tcs.Task;        } 

 

The asynchronous programming model of image based on tasks is explained in the above section. The asynchronous Method should look like-Task-based asynchronous Pattern (TAP ), in the above example, only one call method and one Asynchronous Method are required, and the latter returns a Task or Task <T>. The following describes some of the conventions in the TAP, including how to handle "cancel" and "in progress". We will further explain the task-based programming model. Async and await understand that async methods do not run in their own threads. In fact, writing an async method without any await will be an uncompromising synchronization method:
static async Task<int> TenToSevenAsync()        {            Thread.Sleep(10000);            return 7;        }

 

If you call this method, the thread will be blocked and return 7 after 10 seconds. This may not be what you expected. In VS, you will also get a warning, because this may never be the expected result. Only when an async method runs in an await statement does it immediately return control to the caller. However, only after a waiting task is completed will it actually return results, this means that you need to ensure that the code in the async method does not do too many tasks or block performance calls. The following instance is what you expect.
static async Task<int> TenToSevenAsync(){    await Task.Delay(3000);    return 7;}

 

Task. Delay is actually an asynchronous version of Tread, Sleep. It returns a Task that will be completed within the specified time. Asynchronous methods of time processing programs and asynchronous methods without return values can be created using await from other asynchronous methods, but where does the Asynchronization end? In a client program, an Asynchronous Method is usually initiated by an event. When you click a button, An Asynchronous Method is activated until it is completed, and the event itself is not related to when the method is executed. This is what we usually call "forget after sending" in order to adapt to this mode, the Asynchronous Method is usually explicitly designed to replace the Task <TResult> type with void as the return value, so that the method can be directly used as an event handler. When a void saync method is executed, No Task is returned, and the caller cannot track whether the call is complete.
private async void someButton_Click(object sender, RoutedEventArgs e){    someButton.IsEnabled = false;    await SumPageSizesAsync(GetUrls()));    someButton.IsEnabled = true;}

 


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.