Understanding high performance and high concurrency from the root (4): Go deep into the operating system and thoroughly understand synchronization and asynchrony

The original title of this article "From Xiaobai to Master, You Need to Understand Synchronization and Asynchrony", please contact the author for reprinting.

1. Introduction to the series

1.1 Purpose of the article

As a developer of instant messaging technology, the technical concepts related to high performance and high concurrency have long been clear. What thread pool, zero-copy, multiplexing, event-driven, epoll, etc. are all at your fingertips, maybe you Familiar with technical frameworks with these technical characteristics such as: Java's Netty , Php's workman , Go's gnet, etc. But when it comes to face-to-face or technical practice, when you encounter unresolved doubts, you know that what you have is just the skin.

Return to the basics and return to the essence, what are the underlying principles behind these technical features? How to understand the principles behind these technologies in an easy-to-understand and effortless way is exactly what the series of articles "Understanding High Performance and High Concurrency from the Root" will share.

1.2 Origin of the article

I have compiled a lot of resources and articles related to IM, message push and other instant messaging technologies, from the initial open source IM framework MobileIMSDK to the online version of the classic network programming masterpiece " TCP/IP Detailed Explanation ", and then to the IM development programmatic. The article "One entry is enough for beginners: Develop mobile IM from scratch ", and " Introduction to Network Programming for Lazy People ", " Introduction to Brain Disabled Network Programming ", " High-performance Network Programming ", " Not for Known Network Programming " series of articles.

The more you go to the depths of knowledge, the more you feel that you know too little about instant messaging technology. So later, in order to allow developers to better understand the characteristics of networks (especially mobile networks) from the perspective of basic telecommunications technology, I collected and compiled a series of high-level articles on the " Introduction to Zero-Basic Communication Technology for IM Developers " across disciplines . This series of articles is already the knowledge boundary of network communication technology for ordinary instant messaging developers. With these network programming materials before, it is basically enough to solve the knowledge blind spots in network communication.

For the development of instant messaging systems such as IM, knowledge of network communication is indeed very important, but it returns to the essence of technology to realize these technical characteristics of network communication itself: including the thread pool, zero copy, multiplexing, and multiplexing mentioned above. Event-driven, etc., what is their nature? What is the underlying principle? This is the purpose of organizing this series of articles, I hope it will be useful to you.

1.3 Article directory

" Understanding high performance and high concurrency from the root (1): Going deep into the bottom of the computer, understanding threads and thread pools "

" Understanding high performance and high concurrency from the root (2): In-depth operating system, understanding I/O and zero copy technology "

" Understanding high performance and high concurrency from the root (3): In-depth operating system, thorough understanding of I/O multiplexing "

" Understanding high performance and high concurrency from the root (4): In-depth operating system, thorough understanding of synchronization and asynchrony " (* this article)

"Understanding high performance and high concurrency from the root (5): How do high-concurrency and high-performance servers are implemented (to be released later..)"

1.4 Overview of this article

Continuing from the previous article "In- depth Operating System, Thorough Understanding of I/O Multiplexing ", this article is the fourth article in the high-performance and high-concurrency series. This article will focus on the basics and explain to you what is synchronous and asynchronous. And what these two extremely important concepts mean in programming in high-concurrency, high-performance technology.

This article has been simultaneously published on the "Instant Messaging Technology Circle" official account, welcome to follow. The link on the official account is: click here to enter .

2. The author of this article

At the request of the author, no real names or personal photos are provided.

The main technical direction of the author of this article is the Internet backend, high-concurrency and high-performance server, and search engine technology. The screen name is "Coder's Deserted Island Survival" and the public account "Coder 's Deserted Island Survive ". Thank the author for his selfless sharing.

3. Write in front

I believe that when many students encounter the words "synchronization and asynchrony", their brains instantly fall into a state of confusion like a crossroads where traffic lights fail.

Yes, these two words that look alike and actually are alike have caused a lot of trouble to bloggers. What are the meanings behind these two words?

Let's start with the work scene.

4. Synchronous and asynchronous scenario 1: Hard-working programmer

4.1 Synchronization

Suppose your boss now assigns you a very urgent and important task that you must complete before you leave work (the evil capitalism). In order to supervise the degree, the boss moved a chair and sat while staring at you while writing code.

You must have scolded in your heart: "WTF, are you so free? Staring at Laozi, can't you do something else?"

The boss seemed to have received your brainwaves: "I'll just wait here. I won't go anywhere or the toilet until you finish writing."

In this example, the boss waits after giving you the task, and does nothing until you finish writing. This scenario is called synchronization.

4.2 Asynchronous

The next day, the boss gave you another task.

But this time I’m not so anxious. This time the boss downplayed, “That’s okay, that’s not bad, you’ll work hard for another year, and I’ll be financially free next year. I don’t have to worry about this task today. Just tell me when you finish writing. ".

This time the boss did not stare at you to write the code, but turned around and went to the video. After you finished writing, you simply reported to the boss "I'm done writing".

In this example, after the boss confessed to the task, he no longer waits to do nothing but goes to work on other things. After you complete the task, simply tell the boss that the task is complete. This is the so-called asynchronous.

4.3 Summary

Regarding the above scenario, let’s summarize: In the asynchronous scenario, the focus is that the boss is watching the drama while you are writing the code. The two things are going on at the same time, instead of waiting for the other side, so this is why in general The essence of asynchrony is more efficient than synchronization, no matter what scenario it is used in.

We can see that the word synchronization is often related to the tasks' "dependency", "relationship", "waiting" and other keywords, while asynchrony is often related to the task's "not dependent", "unrelated", "no need to wait", and "at the same time". "Happens" and other keywords are related.

By the way, if you meet a boss who is staring at you writing code behind you, thirty-six strategies are the best strategy.

5. Synchronous and asynchronous scenario 2: Making a call and sending an email

5.1 Synchronization

As a hard-working programmer, you can't just bury your head and move bricks. Communication in daily work cannot be avoided. One of the efficient ways of communication is quarrels. . . Ah no, it's a phone call.

Usually when a call is made, one person is talking and the other person is listening. While one person is talking, the other person waits, and waits for the other person to finish talking before continuing. Therefore, in this scenario, you can see "dependence". Keywords such as, "association", and "waiting" appear, so the communication method of calling is the so-called synchronization.

5.2 Asynchronous

Another communication method commonly used by code farmers is email.

Email is another indispensable way of communication, because no one is waiting for you to write an email and do nothing, so you can write slowly. When you are writing an email, the recipient can do something like catching fish. , Go to the toilet, and at the same time complain about why the November holiday is not for two weeks and other meaningful things.

At the same time, when you finish writing the email and send it out, there is no need to wait for the other party to reply and do nothing. You can also do something meaningful like fishing (^_^).

Here, when you write an e-mail, others are catching fish. These two things are going on at the same time. The recipient and the sender do not need to wait for each other. When the sender finishes writing the e-mail, simply click to send and receive. The recipient can read it after receiving it, and the recipient and the sender do not need to rely on each other or wait for each other.

You see, in this scenario, the keywords "not dependent", "unrelated", and "no need to wait" appear. Therefore, the communication method of email is asynchronous.

6. Synchronous calls in programming

Now finally back to the topic of programming.

Now that we have understood the meaning of synchronization and asynchrony in various scenarios (I hope so), how should programmers understand synchronization and asynchrony?

Let's talk about synchronous calls first, which is the most familiar scenario for programmers.

General function calls are synchronous, like this:

funcA () {

    // Wait for the function funcB to complete

    funcB ();

 

    // continue the next process

}

funcA calls funcB, then the subsequent code in funcA will not be executed before funcB is executed, that is to say, funcA must wait for the execution of funcB to complete.

Just like the picture below:

From the above figure, we can see that funcA can't do anything while funcB is running. This is a typical synchronization.

Note: Generally speaking, like this kind of synchronous call, funcA and funcB are running in the same thread, which is the most common situation.

But it is worth noting that even functions that run in two non-threaded threads can be called synchronously. For example, when we perform IO operations, the bottom layer actually sends requests to the operating system through system calls, such as disk file reading:

read(file, buf);

This is the blocking I/O we described in "In- depth Operating System, Understanding I/O and Zero Copy Technology ". The program cannot move forward until the read function returns:

read(file, buf);

// The program is suspended,

// Wait for the file to be read and continue to run

As shown below:

Only when the read function returns, the program can be continued.

Note: Unlike the above synchronous call, the function and the called function run in different threads.

Therefore: we can conclude that it does not matter whether the synchronous call and the function and the called function run in the same thread.

Here we have to emphasize again: the function and the called function cannot be performed at the same time in the synchronous mode.

Synchronous programming is the most natural and easy to understand for programmers.

But the easy-to-understand price is that in some scenarios, synchronization is not efficient. The reason is simple because there is no way to perform tasks at the same time.

Next we look at asynchronous calls.

7. Asynchronous calls in programming

Where there is a synchronous call, there is an asynchronous call.

If you really understand the contents of this section so far, then asynchronous calls are not a problem for you.

Generally speaking: Asynchronous calls always go hand in hand with time-consuming tasks such as I/O operations, such as disk file reading and writing, network data sending and receiving, database operations, etc.

Let's take disk file reading as an example.

In the synchronous calling mode of the read function, the caller cannot move forward until the file is read, but the situation is different if the read function can be called asynchronously.

If the read function can be called asynchronously, even if the file has not been read, the read function can return immediately.

read(file, buff);

// The read function returns immediately

// will not block the current program

Just like the picture below:

It can be seen that in the asynchronous call mode, the caller will not be blocked, and the next program can be executed immediately after the function call is completed.

At this time, the key point of asynchrony is: the caller's next program execution can be performed at the same time as the file reading. From the above figure, we can also see this. This is the high efficiency of asynchrony.

But: Please note that asynchronous calls are a burden for programmers to understand, and coding is even more a burden. In general, God will close a door appropriately when opening a door for you. window.

Some students may ask, under the synchronous call, the caller no longer continues execution but pauses and waits. After the called function is executed, it is natural for the caller to continue execution, so how does the caller know whether the called function is called under the asynchronous call? Is the execution completed?

This is divided into two situations:

  • 1) The caller does not care about the execution result at all;
  • 2) The caller needs to know the execution result.

The first case is relatively simple and does not need to be discussed.

The second case is more interesting, there are usually two ways to achieve:

  • 1) One is the notification mechanism: when the task is executed, a signal is sent to notify the caller that the task is completed (the signal here can be implemented in many ways: signal in Linux, or using semaphore and other mechanisms can be implemented);
  • 2) One is the callback mechanism: which is what we often call callback (we will focus on callbacks in the next article, and there will be a brief discussion in this article).

Next, we use a specific example to explain synchronous and asynchronous calls.

8. Understand synchronous and asynchronous in specific programming examples

8.1 A concrete example

We use common Web services to illustrate this problem.

Generally speaking, Web Server will have some typical processing logic after receiving a user request. The most common one is database query (Of course, you can also replace the database query here with other I/O operations, such as disk reading, network communication Etc.), here we assume that processing a user request needs to go through steps A, B, and C, and then read the database. After the database is read, it needs to go through steps D, E, and F.

like this:

# Steps to go through to process a user request:

A;

B;

C;

Database read;

D;

E;

F;

Among them: Steps A, B, C and D, E, F do not require any I/O, which means that these six steps do not need to read files, network communications, etc., only the database query step is involved in I/O operations .

Generally speaking: such a Web Server has two typical threads: the main thread and the database processing thread (note: this discussion is only a typical scenario, the specific business may actually be different, but this does not affect our use of two Thread to illustrate the problem).

First of all, let's look at the simplest way to implement it, which is synchronization.

This way is the most natural and easy to understand:

// main thread

main_thread() {

    A;

    B;

    C;

    Send database query request;

    D;

    E;

    F;

}

// database thread

DataBase_thread() {

    while(1) {

        Process database read requests;

        Return result;

    }

}

This is the most typical synchronization method: the main thread will be blocked and suspended after issuing a database query request, until the D, E, and F can continue to run after the database query is completed.

Just like the picture below:

From the above figure, we can see that there will be a "gap" in the main thread. This gap is the "leisure time" of the main thread. During this leisure time, the main thread needs to wait for the database query to complete before continuing the subsequent processing flow.

Here the main thread is like the boss of the supervisor, and the database thread is like the programmer who is forced to move the bricks. The boss does nothing before moving the bricks and just stares at you closely, and waits for you to move the bricks before going to other tasks. thing.

Obviously: efficient programmers cannot tolerate the laziness of the main thread.

It's time to sacrifice a big killer, this is asynchronous:

In the asynchronous implementation scheme, the main thread does not wait for the completion of the database query at all, but directly processes the next request after sending the database read and write requests.

Some students may have questions: A request needs to go through the seven steps of A, B, C, database query, D, E, and F. If the main thread completes A, B, C, and database query, it will be processed directly. Next What about the remaining steps D, E, and F in the previous request?

If you haven't forgotten the content of the previous section, you should know that there are two situations, let's discuss them separately.

8.2 Asynchronous situation 1: The main thread does not care about the results of database operations

In this case, the main thread does not care about whether the database query is completed, and processes the next three steps D, E, and F after the database query is completed.

Just like the picture below:

See it, the next point is here.

We said that a request needs to go through seven steps. The first three are completed in the main thread, and the last four are completed in the database thread. Then how does the database thread know to process D, E, and E after checking the database? What about these steps?

At this time, our other protagonist callback function begins to appear.

Yes, the callback function is used to solve this problem.

We can encapsulate the steps of processing D, E, and F into a function, assuming that the function is named handle_DEF_after_DB_query.

The pseudo code is as follows:

void handle_DEF_after_DB_query () {

    D;

    E;

    F;

}

In this way, the main thread passes the function as a parameter while sending the database query request:

DB_query(request, handle_DEF_after_DB_query);

After the database thread is processed, you can directly call handle_DEF_after_DB_query. This is the function of the callback function.

Some students may also have questions, why should this function be passed to the database thread instead of the database thread defining its own call?

Because from the perspective of the software organization, this is not the job that the database thread should do.

All the database thread needs to do is to query the database and then call a processing function. As for what the processing function does, the database thread does not care at all, nor should it care.

You can pass in a variety of callback functions: that is to say, the database system can program for the abstract function variable of the callback function, so as to better respond to changes, because the content of the callback function will not affect the database thread Logic, and if the database thread defines its own processing function, then this design has no flexibility at all.

From the perspective of software development: Assuming that the database thread logic is encapsulated for the library to provide to other teams, how can the database team know what to do after the database is queried during research and development?

Significant however: only use just know what to do after the database query finished, so the consumer at the time of use Simply pass this callback function on it.

In this way, the team of the complex database and the user team have achieved the so-called decoupling.

Now you should understand the function of the callback function.

In addition: if you look carefully at the above two pictures, can you see why asynchronous is more efficient than synchronous?

The reason is simple. This is what we have mentioned in this article. Asynchrony naturally requires no waiting and no dependencies.

From the previous picture, we can see that the "leisure time" of the main thread is gone, replaced by constant work, work, and work, just like the hard-pressed 996 programmer, and the database thread is not so large. When I am free, it is replaced by work, work, and work.

The main thread processing request and the database processing query request can be performed at the same time. Therefore, from the perspective of system performance, this design can make fuller use of system resources and process requests more quickly; from the user's point of view, the system will respond more quickly .

This is where asynchronous is efficient.

But we should also be able to see: asynchronous programming is not as easy to understand as synchronous, and system maintainability is not as good as synchronous mode.

So is there a way to combine the easy understanding of synchronous mode and the high efficiency of asynchronous mode? The answer is yes, we will explain this technique in detail in subsequent chapters.

Next, we look at the second case, that is, the main thread needs to care about the results of the database query.

8.2 Asynchronous situation 2: The main thread cares about the results of database operations

In this case, the database thread needs to use the notification mechanism to send the query results to the main thread, and the main thread continues to process the second half of the previous request after receiving the message.

Just like the picture below:

From here we can see: ABCDEF several steps are all processed in the main thread, and the main thread also has no "leisure time", but in this case the database thread is relatively idle, and there is no previous one. The method is efficient, but it is still more efficient than the synchronous mode.

The last thing to note is that asynchronous is not necessarily more efficient than synchronous in all cases, and it needs to be analyzed in detail based on the specific business and the complexity of IO.

9. Summary of this article

In this article, we have analyzed the two concepts of synchronization and asynchrony from various scenarios, but no matter what the scenario, synchronization often means that the two parties have to wait and depend on each other, while asynchrony means that the two parties are independent of each other and do their own things. I hope this article will help you understand these two important concepts.

Next article "Understanding High Performance and High Concurrency from the Root (5): How Does High Concurrency and High Performance Server Realize?", stay tuned!

Appendix: More high-performance, high-concurrency articles

" High-performance network programming (1): How many concurrent TCP connections can a single server have "

" High-performance network programming (2): The famous C10K concurrent connection problem in the last 10 years "

" High-Performance Network Programming (3): In the next 10 years, it's time to consider C10M concurrency "

" High-performance network programming (4): Theoretical exploration of high-performance network applications from C10K to C10M "

" High-Performance Network Programming (5): Reading the I/O Model in High-Performance Network Programming in One Article "

" High-Performance Network Programming (6): Understanding the Thread Model in High-Performance Network Programming in One Article "

" High-performance network programming (7): What is high concurrency? Understand in one sentence!

" Take the network access layer design of the online game server as an example to understand the technical challenges of real-time communication "

" Knowing the technology sharing: knowing the practice of high-performance long-connection gateway technology with tens of millions of concurrency "

" Taobao Technology Sharing: The Technological Evolution Road of the Mobile Access Layer Gateway of the Hand Taobao Billion Level "

" A set of mobile IM architecture design practice sharing for massive online users (including detailed graphics and text) "

"An Original Distributed Instant Messaging (IM) System Theoretical Architecture Plan "

" WeChat background based on the time series of massive data cold and hot hierarchical architecture design practice "

" WeChat Technical Director Talks about Architecture: The Way of WeChat-Dao Zhi Jian (Full Speech) "

" How to Interpret "WeChat Technical Director Talking about Architecture: The Way of WeChat-The Road to the Simple" "

" Rapid Fission: Witness the evolution of WeChat's powerful back-end architecture from 0 to 1 (1) "

" 17 Years of Practice: Technical Methodology of Tencent's Massive Products "

" Summary of Tencent's Senior Architect Dry Goods: An article to understand all aspects of large-scale distributed system design "

" Take Weibo application scenarios as an example to summarize the architectural design steps of massive social systems "

" Getting Started: A Zero-Basic Understanding of the Evolution History, Technical Principles, and Best Practices of Large-scale Distributed Architectures "

" From novice to architect, one piece is enough: the evolution of architecture from 100 to 10 million high concurrency "

This article has been simultaneously published on the official account of "Instant Messaging Technology Circle".

▲ The link of this article on the official account is: click here to enter , the original link is: http://www.52im.net/thread-3296-1-1.html

Guess you like

Origin blog.csdn.net/hellojackjiang2011/article/details/112525472
Recommended