16

私の MVC4 アプリでは、大きなファイルをアップロードして処理するためのコントローラーを追加する必要があります。ファイルがアップロードされた直後に、そのファイルの非同期処理を開始し、処理が完了するのを待たずにブラウザーに応答を返す必要があります。

明らかに、ファイルを手動で処理するための新しいスレッドを開始できますが、.net 4.5 で導入された async/await メカニズムを使用してこのシナリオを実装できるかどうか疑問に思っています

コンセプトをテストするために、次のようなことを試しました。

public async Task<ActionResult> Test()
{
    TestAsync();
    return View("Test");
}

public async void TestAsync()
{
    await LongRunning();
}

private Task<int> LongRunning()
{
    return Task<int>.Factory.StartNew(() => Pause());
}

private int Pause()
{
    Thread.Sleep(10000);
    return 3;
}

非同期メカニズムは一般的に機能しているようです。コードをデバッグするときに、「return View("Test");」を押します。行「return 3」の前の行。ただし、ブラウザは Pause メソッドが完了した後にのみ応答を受け取ります。

これは、通常の非同期コントローラー (Async および Completed メソッドを持つもの) のように動作するようです。私のシナリオのコントローラーで async/await を使用する方法はありますか?

4

3 に答える 3

14

明らかに、ファイルを手動で処理するための新しいスレッドを開始できますが、.net 4.5 で導入された async/await メカニズムを使用してこのシナリオを実装できるかどうか疑問に思っています。

asyncは HTTP プロトコルを変更しないため、できません。

Svick と James は既に正しい回答をコメントとして投稿しています。便宜上、以下に複製します。

IIS は、ほぼいつでもアプリケーションをリサイクルできます。

リクエストに対して非同期で長時間実行する作業がある場合は、別の場所で実行してください。「標準」とは、永続的なキュー (MSMQ、Azure、RabbitMQ など) と、それらを処理する他のもの (Windows サービス、タスク スケジューラによって実行される exe、Quartz.net を使用するアプリなど) です。

要約すると、HTTP は 1 つの要求と 1 つの応答を返します ( async-そして他のものは-これを変更しません)。

ASP.NET は HTTP 要求を中心に設計されています (また、「未処理の要求がない場合は、この Web サイトを安全に停止できる」などの前提を置いています)。新しいスレッドを開始して、アップロードをメモリに保持することができます (これが最も簡単な方法です) が、強くお勧めしません。

あなたの状況では、James の提案に従うことをお勧めします。

  • アップロードが完了したら、アップロードを永続的なストレージ (Azure キューなど) に保存し、"チケット" をブラウザーに返します。
  • 他のプロセス (Azure ワーカー ロールなど) でキューを処理し、最終的に完了としてマークします。
  • ブラウザに「チケット」でサーバーをポーリングさせます。サーバーは「チケット」を使用して永続ストレージ内のファイルを検索し、ファイルが完了しているかどうかを返します。

これにはいくつかのバリエーションがあります (たとえば、SignalR を使用して処理が完了したときにブラウザーに通知する)、一般的なアーキテクチャは同じです。

これは複雑ですが、正しい方法です。

于 2012-09-07T18:45:17.190 に答える
2

コードにエラーがあります。アクションでTestAsyncの呼び出しを待つ必要があります。そうしないと、何も実行せずに「タスク」オブジェクトを返すだけです。

public async Task<ActionResult> Test()
{
    await TestAsync();
    return View("Test");
}

非同期メソッドでスリープする正しい方法は、

await Task.Delay(10000);

TestAsyncのシグネチャを変更する必要があります。voidを返す場合は、メソッドに非同期のマークを付けないでください。代わりにTaskを返す必要があります。voidを返すことは、.netイベントとの互換性のために予約されています。

public async Task TestAsync()
{
    await LongRunning();
}

メソッド本体は同じです。

于 2012-09-14T14:01:50.590 に答える
0

あなたの LongRunning メソッドは同期的に 10 秒間スリープしています。代わりにタスクでスリープが発生するように変更します。

于 2012-09-07T14:15:03.247 に答える