Multithreading programming learning notes-using asynchronous IO (I), multithreading programming io
Multi-thread programming learning notes-using concurrent sets (1)
Multi-thread programming learning notes -- using concurrent sets (2)
Multi-thread programming learning notes-using concurrent sets (3)
In the following scenario, if you run a program on the client, one of the most important tasks is to have a response user interface. This means that no matter what happens to the application, all user interface elements must run quickly, and users can get quick responses from the application. It is not easy to achieve this! If you try to open notepad in Windows and load a document with a size of several megabytes, the application window will be closed for a while, because the entire file must be loaded from the hard disk first, then the program can start processing user input.
This is a very important issue. In this case, the only solution is to avoid blocking the UI pub. This in turn means that in order to prevent blocking of the UI thread, each UI-related API must be allowed to be called asynchronously only. This is the key reason for the Windows operating system to re-upgrade the API. Almost every method is replaced with an Asynchronous Method. But will the performance be affected when applications use multithreading to achieve this goal? Of course. However, considering that there is only one user, this is cost-effective. This is a good thing if applications can use all the capabilities of the computer to become more efficient and only serve the only user who runs the program.
Next let's look at the second case. If the program runs on the server, it is totally different. Scalability is the highest priority, which means that the less resources a single user consumes, the better. If multiple threads are created for each user, the scalability is not good. Balancing the consumption of application resources in an efficient way is a very complicated problem. For example, in ASP. NET, we use a working thread pool to serve client requests. The worker threads in this pool are limited, so we have to minimize the time used by each worker thread to achieve high scalability. This means that the worker thread needs to be put back to the pool faster and better, so as to serve the next request. If we start an asynchronous operation that requires computing, the entire workflow will be inefficient. First, a working thread is extracted from the thread pool to serve client requests. Then extract another worker thread and start processing asynchronous operations. Now there are two working threads that are processing requests. If the first thread can do something useful, it is very good. Unfortunately, we usually wait for the asynchronous operation to complete, but we consume two working threads instead of one. In this scenario, Asynchronization is actually worse than synchronous execution! We don't need to use all the CPU cores, because we are already serving many clients and they have used all the computing power of the CPU. We do not need to maintain the first thread response because there is no user interface. So why should we use Asynchronization on the server?
The answer is that only asynchronous input/output operations can be used asynchronously. Currently, modern computers use a disk drive to store files and a network card to send and receive data over the network. All these devices have their own chips that manage input/output operation signals to the operating system in a very underlying manner. This method of executing an I/O task is called an I/O thread.
In ASP. NET, an asynchronous I/O operation is immediately returned to the thread pool at the beginning of the working thread. When this operation continues, this thread can serve other clients. Finally, when the operation signal is completed, the ASP. NET infrastructure obtains an idle working thread from the thread pool and then completes the operation.
I. Use Files Asynchronously
This tutorial explains how to read and write a file asynchronously.
1. The sample code is as follows.
Using System; using System. collections. generic; using System. IO; using System. linq; using System. text; using System. threading. tasks; namespace ThreadIODemo {class Program {static void Main (string [] args) {Console. writeLine ("-- start using asynchronous I/O threads --"); var t = ReadWriteAsyncIO (); t. getAwaiter (). getResult (); Console. read ();} const int BUFFER_SIZE = 4096; async static Task ReadWriteAsyncIO () {using (var fs = new FileStream ("test1.txt", FileMode. create, FileAccess. readWrite, FileShare. none, BUFFER_SIZE) {Console. writeLine ("1. whether the I/O thread is used asynchronously: {0} ", fs. isAsync); byte [] buffer = Encoding. UTF8.GetBytes (CreateFileContent (); var writeTask = Task. factory. fromAsync (fs. beginWrite, fs. endWrite, buffer, 0, buffer. length, null); await writeTask;} using (var fs = new FileStream ("test2.txt", FileMode. create, FileAccess. readWrite, FileShare. none,
BUFFER_SIZE, FileOptions. asynchronous) {Console. writeLine ("2. whether the I/O thread is used asynchronously: {0} ", fs. isAsync); byte [] buffer = Encoding. UTF8.GetBytes (CreateFileContent (); var writeTask = Task. factory. fromAsync (fs. beginWrite, fs. endWrite, buffer, 0, buffer. length, null); await writeTask;} using (var fs = File. create ("test3.txt", BUFFER_SIZE, FileOptions. asynchronous) {using (var sw = new StreamWriter (fs) {Console. writeLine ("3. whether the I/O thread is used asynchronously: {0} ", fs. isAsync); await sw. writeAsync (CreateFileContent () ;}} using (var sw = new StreamWriter ("test4.txt", true) {Console. writeLine ("4. whether the I/O thread is used asynchronously: {0} ", (FileStream) sw. baseStream ). isAsync); await sw. writeAsync (CreateFileContent ();} System. threading. thread. sleep (1, 1000); Console. writeLine ("START asynchronous file reading"); Task <long> [] readTasks = new Task <long> [4]; for (int I = 0; I <4; I ++) {readTasks [I] = SumFileContent (string. format ("test1000002.16.txt", I + 1);} long [] sums = await Task. whenAll (readTasks); Console. writeLine ("all files and values: {0}", sums. sum (); Console. writeLine ("Start deleting files"); Task [] delTasks = new Task [4]; for (int I = 0; I <4; I ++) {string filename = string. format ("test1000002.16.txt", I + 1); delTasks [I] = SimulateAsynchronousDelete (filename);} await Task. whenAll (delTasks); Console. writeLine ("End of deleting a file");} static string CreateFileContent () {var sb = new StringBuilder (); for (int I = 0; I <100000; I ++) {sb. appendFormat ("{0}", new Random (I ). next (0, 99999); sb. appendLine ();} return sb. toString ();} async static Task <long> SumFileContent (string filename) {using (var fs = new FileStream (filename, FileMode. open, FileAccess. read, FileShare. none, BUFFER_SIZE, FileOptions. asynchronous) using (var sr = new StreamReader (fs) {long sum = 0; while (sr. peek ()>-1) {string line = await sr. readLineAsync (); sum + = long. parse (line) ;}return sum ;}} static Task SimulateAsynchronousDelete (string filename) {return Task. run () => File. delete (filename ));}}}
2. The program running result, such.
When the program runs, we create four files in different ways and write some random data.
In the first example, the FileStream class and its method are used to convert the asynchronous programming mode API into a task.
In the second example, the FileStream class and its method are used. However, the FileStream. Asynchronous parameter is provided during the construction.
In the third example, some simplified APIs are used, such as the File. Create method and StreamWrite class. It also uses the I/O thread. We can use the Stream. iSaSYNC attribute to check.
The fourth example shows that over-simplification is not good. Here we use Asynchronous delegate calls to simulate asynchronous I/O. In fact, asynchronous I/O is not used.
Then, read data from all files asynchronously in parallel, calculate the content of each file, and calculate the sum.
Finally, delete all objects.