web-dev-qa-db-ja.com

Node.jsと.netのAsync / await

Node.jsの非同期モデル(非ブロッキングスレッド)と他の言語の違いは何ですか?私を説明/リダイレクトできますか?これは、どちらも同じモデルであるように見えます。お勧めします。

46
PKV

両方のモデルは非常に似ています。主に2つの違いがありますが、そのうちの1つはまもなく廃止されます(「間もなく」の定義)。

1つの違いは、Node.jsは非同期的にシングルスレッド化され、ASP.NETは非同期的にマルチスレッド化されることです。 allコードは常に同じ正確なスレッドで実行されるため、Node.jsコードはいくつかの単純化された仮定を行うことができます。したがって、ASP.NETがawaitsをコード化すると、おそらくdifferentスレッドで再開できます。スレッドローカル状態などを回避するのはユーザー次第です。

ただし、async ASP.NETはサーバーの全機能をすぐに利用できるので、この同じ違いはASP.NETの強みでもあります。たとえば、8コアのマシンを検討すると、ASP.NETは8つのリクエスト(の同期部分)を同時に処理できます。 Node.jsを強化されたサーバーに配置する場合、実際にはNode.jsの8つの個別のインスタンスを実行し、nginxまたはそのサーバーのルーティング要求を処理する単純なカスタムロードバランサーを追加するのが一般的です。これは、サーバー全体で共有される他のリソース(キャッシュなど)が必要な場合は、それらもプロセス外に移動する必要があることを意味します。

他の大きな違いは、実際にはプラットフォームではなく言語の違いです。 JavaScriptの非同期サポートは、コールバックとプロミスに限定されており、最高のライブラリを使用しても、些細なことをしないと本当に厄介なコードになってしまいます。対照的に、C#/ VBのasync/awaitのサポートにより、非常に自然な非同期コードを作成できます(さらに重要なことは、maintainable非同期コード)。

ただし、言語の違いはなくなります。 JavaScriptの次のリビジョンでは、ジェネレーターを導入します。これは、ヘルパーライブラリとともに、async/awaitを使用して、Node.jsの非同期コードを今日と同じように自然なものにします。 「近日公開」のものを今すぐプレイしたい場合は、V8 3.19でジェネレーターが追加され、Node.js 0.11.2(Unstableブランチ)にロールバックされました。パス --harmonyまたは--harmony-generatorsジェネレーターのサポートを明示的に有効にします。

78
Stephen Cleary

Node.jsの非同期モデルとC#の非同期モデルの違い 非同期/待機 モデルは巨大です。 Node.jsを持つ非同期モデルは、 古い Event-based Asynchronous Pattern(EAP)と呼ばれるC#および.Netの非同期モデル。 C#と.Netには3つの非同期モデルがあり、それらについては Asynchronous Programming Patterns で読むことができます。 C#の最新の非同期モデルは、C#のタスクベースです 非同期 そして 待つ キーワードについては、 タスクベースの非同期パターン で読むことができます。 C#の 非同期/待つ キーワードを使用すると、非同期コードが線形になり、他のプログラミング言語よりもはるかに優れた「コールバック地獄」を回避できます。試してみるだけで、その後は他の方法でそれを行うことはありません。非同期操作を消費するコードを書くだけで、他のコードを書くように見えるので、読みやすさを心配しません。こちらの動画をご覧ください:

  1. 非同期プログラミングの詳細
  2. ASP.NETの非同期
  3. 非同期および待機可能なタスクを理解する

そして、C#とNode.jsの両方で非同期の何かを実行して比較してください。違いがわかります。

編集:Node.js V8 JavaScriptエンジンはジェネレーターをサポートしているため、 ECMAScript 6 Draftで定義 、JavaScriptコードの「コールバック地獄」も簡単に回避できます。 JavaScriptでasync/awaitの何らかの形を実現します

16
Ashot Muradian

Nodejsでは、すべてのリクエストはイベントキューに入れられます。ノードのイベントループは、単一のスレッドを使用してイベントキューのアイテムを処理し、すべての非IO作業を行い、すべてのIOバインド作業をC++スレッドプールに送信します(javascriptコールバックを使用して非同期を管理します)。 C++スレッドは、その結果をイベントキューに追加します。

ASP.NETとの違い(最初の2つは、非同期IOを許可するすべてのWebサーバーにほぼ当てはまります):

  1. ASP.NETは、着信要求ごとに異なるスレッドを使用するため、コンテキスト切り替えのオーバーヘッドが発生します
  2. .NETは、非同期を使用してIOにバインドされた作業を行うことを強制しないため、IOにバインドされたAPI呼び出しが事実上非同期である(コールバックを使用する)nodejsほど慣用的ではありません。
  3. .NETの「await-async」addは、コンパイル時に「コールバック」を追加するためのステップであるため、nodejsとは対照的に、線形コード(コールバック関数を渡さない)を記述できます。

Webにはノードのアーキテクチャを説明する場所がたくさんありますが、ここに何かあります: http://johanndutoit.net/presentations/2013/02/gdg-capetown-nodejs-workshop-23-feb-2013/index .html#1

6
billy

Nodejsと.NETの非同期の違いは、ユーザーコードにプリエンプティブマルチタスクを使用することです。 .NETはユーザーコードにプリエンプティブマルチタスクを使用しますが、Nodejsは使用しません。

Nodejsは、IOリクエストを処理するために内部スレッドプールを使用し、IOコールバックを含む、JSコードを実行するための単一スレッドを使用します。

プリエンプティブマルチタスク(.NET)を使用すると、スタックの実行中に別の実行スタックによって共有状態が変更される可能性があります。 Nodejsには当てはまりません-非同期操作からのコールバックは、現在実行中のスタックと同時に実行できません。別の実行スタックはJavascriptには存在しません。非同期操作の結果は、現在の実行スタックが完全に終了した場合にのみコールバックで利用できます。この場合、現在のスタックは終了せず、次のループは開始されないため、単純なwhile(true);がNodejsをハングさせます。

違いを理解するために、js用とnet用の2つの例を検討してください。 var p = new Promise(function(resolve){setTimeout(resolve、500、 "my content");}); p.then(function(value){// ... value === "my content"

このコードでは、非同期操作を「開始」した後、現在のコールスタック全体が終了するまで非同期操作によって開始されるコールバックコードが実行されないことが確実であるため、ハンドラーを安全に配置できます。コールバックは次のサイクルで処理されます。タイマーコールバックについては、同じように扱われます。非同期タイマーイベントは、次のサイクルで処理されるコールバック処理をキューに配置します。

.NETでは異なります。サイクルはありません。プリエンプティブマルチタスクがあります。

ThreadPool.QueueUserWorkItem((o)=>{eventSource.Fire();});
eventSource.Fired += ()=>{
 // the following line might never execute, because a parallel execution stack in a thread pool could have already been finished by the time the callback added.
 Console.WriteLine("1");
}

ノードと同様に、シングルスレッドで非同期処理を行い、非同期IOにスレッドプールを使用するHello World .NET a-la Nodejsコードを次に示します。 (.NETにはasync IO操作のTPLバージョンとIAsyncResultバージョンが含まれていますが、この例の目的には違いはありません。とにかくすべてがスレッドプールの異なるスレッドになります。)

void Main()
{
    // Initializing the test
    var filePath = Path.GetTempFileName();
    var filePath2 = Path.GetTempFileName();
    File.WriteAllText(filePath, "World");
    File.WriteAllText(filePath2, "Antipodes");

    // Simulate nodejs
    var loop = new Loop();

    // Initial method code, similar to server.js in Nodejs. 
    var fs = new FileSystem();

    fs.ReadTextFile(loop, filePath, contents=>{
        fs.WriteTextFile(loop, filePath, string.Format("Hello, {0}!", contents),
            ()=>fs.ReadTextFile(loop,filePath,Console.WriteLine));
    });

    fs.ReadTextFile(loop, filePath2, contents=>{
        fs.WriteTextFile(loop, filePath2, string.Format("Hello, {0}!", contents),
            ()=>fs.ReadTextFile(loop,filePath2,Console.WriteLine));
    });

    // The first javascript-ish cycle have finished.

    // End of a-la nodejs code, but execution have just started.

    // First IO operations could have finished already, but not processed by callbacks yet

    // Process callbacks
    loop.Process();

    // Cleanup test
    File.Delete(filePath);
    File.Delete(filePath2);
}

public class FileSystem
{
    public void ReadTextFile(Loop loop, string fileName, Action<string> callback)
    {
        loop.RegisterOperation();
        // simulate async operation with a blocking call on another thread for demo purposes only.
        ThreadPool.QueueUserWorkItem(o=>{
            Thread.Sleep(new Random().Next(1,100)); // simulate long read time

            var contents = File.ReadAllText(fileName);
            loop.MakeCallback(()=>{callback(contents);});
        });
    }

    public void WriteTextFile(Loop loop, string fileName, string contents, Action callback)
    {
        loop.RegisterOperation();
        // simulate async operation with a blocking call on another thread for demo purposes only.
        ThreadPool.QueueUserWorkItem(o=>{
            Thread.Sleep(new Random().Next(1,100)); // simulate long write time

            File.WriteAllText(fileName, contents);
            loop.MakeCallback(()=>{callback();});
        });
    }
}

public class Loop
{
    public void RegisterOperation()
    {
        Interlocked.Increment(ref Count);
    }
    public void MakeCallback(Action clientAction)
    {
        lock(sync)
        {
            ActionQueue.Enqueue(()=>{clientAction(); Interlocked.Decrement(ref Count);});
        }
    }

    public void Process()
    {
        while(Count > 0)
        {
            Action action = null;
            lock(sync)
            {
                if(ActionQueue.Count > 0)
                {
                    action = ActionQueue.Dequeue();
                }
            }

            if( action!= null )
            {
                action();
            }
            else
            {
                Thread.Sleep(10); // simple way to relax a little bit.
            }
        }
    }

    private object sync = new object();

    private Int32 Count;

    private Queue<Action> ActionQueue = new Queue<Action>();
}
2
George Polevoy