同期と非同期がこの記事1つでまるわかり!

多くの学生が同期と非同期という 2 つの単語に遭遇すると、脳は信号が消えた交差点のように瞬時に混乱状態に陥ると思います。

はい、非常に似ているように見えて実際には非常に似ているこの 2 つの単語は、ブロガーにとって多くのトラブルを引き起こしていますが、この 2 つの単語の背後にある意味は何でしょうか?

まずは作業風景から。

勤勉なプログラマー

今、あなたの上司が、仕事を辞める前に完了しなければならない非常に緊急かつ重要な仕事をあなたに割り当てたとします(悪の資本主義)。進捗状況を監督するために、上司は椅子を移動し、脇に座ってあなたがコードを書くのを見ていました。あなたは心の中で悪態をついていたでしょう、「なんてこと、そんなに暇なんですか?私を見つめながら他のことができないの?」 上司はあなたの脳波を受信したようです。「私はここで待っているだけです」 「あなたが書き終わるまで、私はどこにも行かないよ、トイレにも行かないよ。」

この例では、上司はあなたにタスクを与えた後も待ち続け、あなたが書き終わるまで何もしません。このシナリオは同期と呼ばれます。翌日、上司から別の仕事が与えられます。しかし、今回はそれほど不安はなく、今度は上司が軽く言ってくれました。書き終わったら教えてください。」今回は上司はあなたがコードを書いているところを見ていたわけではなく、振り向いてビデオを見ていたので、書き終わったら上司に「終わりました」と報告するだけでした。

この例では、上司がタスクを完了した後、何もせずに別の作業に取り掛かります。タスクが完了したら、上司にタスクが完了したことを伝えるだけです。これを非同期と呼びます。非同期シナリオでは、コードを書いている間、上司がショーを見ていることに焦点が当てられることは注目に値します。一方が他方を待つのではなく両方のことが同時に起こっているので、これが非同期である理由です。一般に、同期アプリケーションよりも効率的です。同期アプリケーションと非同期アプリケーションがどのようなシナリオで使用されるかに関係なく、その本質は です。同期という言葉はタスクの「依存」「関連」「待機」などのキーワードと関連していることが多いのに対し、非同期はタスクの「依存していない」「」などのキーワードと関連していることが多いことがわかります。 「無関係」「待つ必要なし」「同時」「発生」などの関連キーワード。ちなみに、コードを書いているところを後ろから見ている上司に遭遇した場合は、三十六計が最適な戦略です。

電話をかける、メールを送信する

勤勉なプログラマーとして、レンガ作りに没頭するわけにはいきません。日々の仕事でコミュニケーションは避けられません。最も効率的なコミュニケーション方法の 1 つは口論です。あ、いや、電話ですよ。

通常、電話をかけるときは、一人が話していて、もう一人が聞いている状態ですが、一人が話している間、もう一人は待機し、相手が話し終わってから話を続けるという流れになります。 「依存」、「相関」、「待ち」などのキーワードが出てくるので、電話をかけるコミュニケーション方法はいわゆる同期です。

プログラマー間でのもう 1 つの一般的な通信方法は電子メールです。電子メールはもう 1 つの重要なコミュニケーション方法であり、あなたが電子メールを書いて何もしないのを待っている人は誰もいないので、ゆっくりとゆっくりと書くことができ、電子メールを書いている間、受信者は魚のようなことをすることができます。トイレに行くと同時に、なぜ国慶節の休暇が2週間も無いのかなど、意味深なことについて不平を言います。同時に、メールを書き終えて送信した後は、相手の返信を待って何もする必要がなく、釣りなどの有意義な時間を過ごすこともできます。

ここでは、あなたが電子メールを書き、他の人がそれを利用します。この 2 つのことが同時に行われます。受信者も送信者もお互いを待つ必要はありません。送信者は電子メールを書き終えたら、[送信] をクリックするだけです。 . 送信者は受信後に読むことができ、受信者と送信者はお互いに依存したり、お互いを待ったりする必要はありません。このシナリオでは、「依存しない」「無関係」「待つ必要がない」というキーワードが出てきますので、メールという通信方法は非同期です。

同期呼び出し

さて、ようやくプログラミングの話に戻ります。さまざまなシナリオにおける同期と非同期の意味を理解できたので (そう願っています)、プログラマは同期と非同期をどのように理解すればよいでしょうか? まず、プログラマーにとって最も馴染みのあるシナリオである同期呼び出しについて説明します。一般的な関数呼び出しは次のように同期的です。

funcA() {
// 等待函数funcB执行完成
    funcB();

// 继续接下来的流程
}

funcA が funcB を呼び出すと、funcB の実行が完了するまで funcA の後続のコードは実行されません。つまり、funcA は、次のように funcB の実行が完了するまで待機する必要があります。

上の図から、funcB の実行中は funcA は何もできないことがわかりますが、これは典型的な同期です。

一般に、このような同期呼び出しの場合、funcA と funcB は同じスレッドで実行され、これが最も一般的な状況であることに注意してください。ただし、2 つの異なるスレッドで実行されている関数であっても同期的に呼び出すことができることは注目に値します。IO 操作を実行するとき、最下層は実際に、ディスク ファイルの読み取りなどのシステム コールを通じてオペレーティング システムにリクエストを作成します。

read(file, buf);

これは I/O をブロックしています。読み取り関数が戻るまでプログラムは先に進むことができません。

read(file, buf);
// 程序暂停运行,
// 等待文件读取完成后继续运行

写真が示すように:

読み取り関数が戻った後でのみ、プログラムは実行を続行できます。上記の同期呼び出しとは異なり、関数と呼び出される関数は異なるスレッドで実行されることに注意してください。したがって、同期呼び出しは、関数と呼び出される関数が同じスレッドで実行されるかどうかとは関係がないと結論付けることができます。ここで、関数と呼び出される関数は同期モードでは同時に実行できないことをもう一度強調しておきます。同期プログラミングは、プログラマにとって最も自然で理解しやすいものです。ただし、わかりやすい代償として、シナリオによっては同期が効率的ではないことが挙げられます。その理由は簡単で、タスクを同時に実行できないためです次に、非同期呼び出しについて見ていきます。

   Information Direct: Linux カーネル ソース コード テクノロジ学習ルート + ビデオ チュートリアル カーネル ソース コード

Learning Express: Linux カーネル ソース コード メモリ チューニング ファイル システム プロセス管理 デバイス ドライバー/ネットワーク プロトコル スタック

非同期呼び出し

同期呼び出しと非同期呼び出しがあります。このセクションでこれまで読んだ内容を本当に理解していれば、非同期呼び出しは問題にならないでしょう。一般に、非同期呼び出しは、ディスク ファイルの読み取りと書き込み、ネットワーク データの送受信、データベース操作などの I/O 操作などの時間のかかるタスクと常に連携して行われます。ここでもディスク ファイルの読み取りを例に挙げてみましょう。読み取り関数の同期呼び出しモードでは、呼び出し元はファイルが読み取られるまで先に進むことはできませんが、読み取り関数を非同期で呼び出すことができる場合は状況が異なります。読み取り関数を非同期で呼び出すことができる場合、ファイルが読み取られていない場合でも、読み取り関数はすぐに戻ることができます。

read(file, buff);
// read函数立即返回
// 不会阻塞当前程序

このような:

非同期呼び出し方式では呼び出し元がブロックされず、関数呼び出し終了後すぐに次のプログラムを実行できることがわかります。このときの非同期のポイントは、上図からもわかるように、ファイルの読み込みと同時に呼び出し側の後続プログラムの実行ができること、これが非同期の効率の良さです。ただし、非同期呼び出しはプログラマにとって理解という点で負担であり、コード作成ではさらに負担となることに注意してください。一般に、神があなたのためにドアを開けるときは、適切にドアを閉めてくれるでしょう。学生の中には、同期呼び出しでは、呼び出し元は実行を続行せず、一時停止して待機するのではないかと尋ねる人もいるかもしれません。呼び出された関数が実行された後、呼び出し元が実行を続けるのは自然なことです。では、非同期呼び出しでは、呼び出し元はどうやってそれを知ることができるのでしょうか。呼び出された関数は実行が完了しましたか? これは 2 つの状況に分けられます。

  1. 呼び出し元は実行結果をまったく気にしません
  2. 呼び出し元は実行結果を知る必要がある

最初のケースは比較的単純であり、説明する必要はありません。2 番目のケースは、より興味深いものです。通常、実装方法は 2 つあります。1 つは通知メカニズムです。これは、タスクの実行が完了すると、呼び出し元にタスクが完了したことを通知するシグナルが送信されることを意味します。実装方法は多数あることに注意してください。 Linux シグナルのメソッド、またはセマフォなどのメカニズムを使用したシグナルのメソッドをここに示します。もう 1 つはコールバックです。これは私たちがよくコールバックと呼んでいます。コールバックについては次の記事で焦点を当て、この記事では簡単に説明します。次に、具体例を用いて同期呼び出しと非同期呼び出しについて説明します。

同期 VS 非同期

この問題を説明するために、一般的な Web サービスを例に挙げます。一般的に、Web サーバーはユーザー リクエストを受信した後、いくつかの典型的な処理ロジックを持ちます。最も一般的なものはデータベース クエリです (もちろん、ここでデータベース クエリをディスク読み取りやネットワークなどの他の I/O 操作に置き換えることもできます)ここでは、ユーザー リクエストの処理には、ステップ A、B、C を経てデータベースを読み取る必要があると仮定します。データベースの読み取りが完了した後、ステップ D、E、F を経る必要があります。 、 このような:

# 处理一次用户请求需要经过的步骤:
A;
B;
C;
数据库读取;
D;
E;
F;

ステップ A、B、C および D、E、F は I/O を必要としません。つまり、これら 6 つのステップではファイルの読み取り、ネットワーク通信などが必要ありません。データベース クエリのステップのみが I/O 操作を必要とします。一般に、このような Web サーバーにはメイン スレッドとデータベース処理スレッドという 2 つの典型的なスレッドがあります。この説明は典型的なシナリオにすぎないことに注意してください。具体的なビジネスは実際には異なる場合がありますが、これは 2 つのスレッドの使用には影響しません。問題を説明するために。まず、最も単純な実装である同期を見てみましょう。この方法は最も自然で理解しやすいです。

// 主线程
main_thread() {
    A;
    B;
    C;
    发送数据库查询请求;
    D;
    E;
    F;
}
// 数据库线程
DataBase_thread() {
while(1) {
        处理数据库读取请求;
        返回结果;
    }
}

これは最も一般的な同期方法です。データベース クエリ リクエストを発行した後、メイン スレッドはブロックされ、データベース クエリが完了するまで一時停止されます。次のように、D、E、および F は実行を継続できます。

この図から、メインスレッドに「ギャップ」があることがわかります。このギャップはメインスレッドの「余暇時間」です。この余暇時間の間、メインスレッドはデータベースの完了を待つ必要があります。後続の処理プロセスを続行する前にクエリを実行します。ここで、メイン スレッドは作業を監督する上司のようなもので、データベース スレッドはレンガを動かすために一生懸命働くプログラマーのようなものです。ボスはレンガが動かされるまで何もせず、ただあなたをじっと見つめ、あなたが移動するのを待ちます。他の作業に移る前にレンガの移動を終えてください。明らかに、有能なプログラマはメインスレッドがlazyであることを許容できません。非同期という大きな武器を使うときが来ました。非同期実装スキームでは、メインスレッドはデータベース クエリの完了をまったく待機せず、データベースの読み取りおよび書き込みリクエストを送信した後、次のリクエストを直接処理します。質問がある学生もいるかもしれません。リクエストは 7 つのステップを通過する必要があります: A、B、C、データベース クエリ、D、E、F。メイン スレッドが A、B、C、データベースの完了後に次のステップを直接処理する場合query.request の場合、前のリクエストの残りのステップ D、E、および F はどうなりますか? 前のセクションの内容を忘れていない場合は、2 つの状況があることを知っているはずです。それぞれについて説明します。

1. メインスレッドはデータベース操作の結果を気にしません。この場合、メインスレッドはデータベースクエリが完了したかどうかをまったく気にしません。データベースクエリが完了した後、次の 3 つのステップ D、E を処理します。 、および F 自体は次のようになります。

ここが重要なポイントです。

リクエストは 7 つのステップを通過する必要があると述べましたが、そのうち最初の 3 つはメイン スレッドで完了し、最後の 4 つはデータベース スレッドで完了します。では、データベース スレッドは D、E、何を処理するかをどのようにして認識するのでしょうか。これらの手順は?この時点で、もう 1 つの主人公のコールバック関数が表示され始めます。はい、この問題を解決するためにコールバック関数が使用されます。D、E、F の処理ステップを関数にカプセル化できます。関数の名前が handle_DEF_after_DB_query であると仮定します。

void handle_DEF_after_DB_query () {
    D;
    E;
    F;
}

このようにして、メインスレッドはデータベースクエリリクエストを送信するときに関数をパラメータとして渡します

DB_query(request, handle_DEF_after_DB_query);

データベース スレッドの処理が終了したら、handle_DEF_after_DB_query を直接呼び出すだけでよく、これがコールバック関数の役割です。学生の中には、なぜこの関数をデータベース スレッド自体で定義して呼び出すのではなく、データベース スレッドに渡す必要があるのか​​と疑問を持つ人もいるかもしれません。ソフトウェア組織構造の観点から見ると、これはデータベース スレッドが行うべき作業ではないためですデータベース スレッドが行う必要があるのは、データベースにクエリを実行し、処理関数を呼び出すことだけです。この処理関数が何を行うかについては、データベース スレッドはまったく気にしませんし、気にする必要もありませんさまざまなコールバック関数を渡すことができます。言い換えれば、コールバック関数の内容の変更はデータベース スレッドのロジックに影響を与えず、データベース スレッドが独自の処理関数を定義すると、この設計には柔軟性がありません。ソフトウェア開発の観点から、データベース スレッド ロジックがカプセル化され、ライブラリ用に他のチームに提供されていると仮定すると、データベース チームは、開発中にデータベース クエリの後に何をすべきかをどのように知ることができるのでしょうか? 明らかに、データベースのクエリ後に何をすべきかはユーザーだけが知っているため、ユーザーは使用時にこのコールバック関数を渡すだけで済みます。このようにして、複雑なデータベース チームはユーザー チームからのいわゆる分離を実現します。ここで、コールバック関数の役割を理解する必要があります。

また、上の 2 つの図をよく見てください。非同期が同期よりも効率的である理由がわかりますか? 理由は非常に簡単で、この記事で説明したとおりであり、非同期には当然待機や依存関係が必要ありません。前の図から、メイン スレッドの「余暇時間」がなくなり、勤勉な 996 プログラマーと同じように、絶え間なく作業、作業、作業が行われ、データベース スレッドがそれほど長くないことがわかります。は仕事、仕事、仕事に置き換えられます。

リクエストを処理するメインスレッドとクエリリクエストを処理するデータベースを同時に実行できるため、システムパフォーマンスの観点からは、この設計によりシステムリソースを最大限に活用し、リクエストをより高速に処理できます。レスポンスも早くなります。これが非同期の効率です。ただし、非同期プログラミングは同期ほど理解しにくく、システムの保守性も同期モードほど良くないことも理解できるはずです。では、同期モードのわかりやすさと非同期モードの効率性を組み合わせる方法はあるのでしょうか? 答えは「はい」です。このテクノロジーについては後続の章で詳しく説明します。次に、2 番目の状況を見てみましょう。つまり、メインスレッドはデータベース クエリの結果を考慮する必要があります。

2. メイン スレッドはデータベース操作の結果を気にします。この場合、データベース スレッドは通知メカニズムを使用してクエリ結果をメイン スレッドに送信する必要があります。メッセージを受信した後、メイン スレッドはメッセージの後半の処理を続行します。前のリクエストは次のようになります。

ここから、ABCDEF のステップはすべてメイン スレッドで処理されていることがわかります。同時に、メイン スレッドにも「余暇」はありませんが、この場合、データベース スレッドは比較的余裕があります。これまでの方法はありません。この方法は効率的ですが、それでも同期モードよりも効率的です。最後に、非同期は必ずしもすべての場合において同期よりも効率的であるわけではなく、特定のビジネスや IO の複雑さに基づいて分析する必要があることに注意してください。

要約する

この記事では、さまざまなシナリオから同期と非同期の 2 つの概念を分析しますが、どのようなシナリオであっても、同期は多くの場合、両方の当事者がお互いを待ち、相互に依存する必要があることを意味し、非同期は両方の当事者が独立していることを意味します。お互いに自分のことをやります。

原作者:『コーダーの無人島サバイバル』

おすすめ

転載: blog.csdn.net/youzhangjing_/article/details/132739158