ASP.NET WebページへのHTTP呼び出しを使用してさまざまな理由でトリガーする必要がある非同期操作があります。私のページが要求されると、この操作が開始され、すぐにクライアントに確認応答が返されます。
このメソッドは、WCF Webサービスを介して公開され、完全に機能します。
私の最初の試みでは、例外がスローされ、私に言った:
このコンテキストでは、非同期操作は許可されません。 非同期操作を開始するページでは、Async 属性をtrueに設定する必要があり、非同期操作は前のページでのみ 開始できますPreRenderCompleteイベントへ。
そのため、もちろんAsync="true"
パラメーターを@Page
ディレクティブに追加しました。現在、エラーは発生していませんが、非同期操作が完了するまでページはブロックされています。
どうすれば真のファイアアンドフォーゲットページを機能させることができますか?
編集:詳細についてはいくつかのコード。これより少し複雑ですが、そこに一般的な考え方を取り入れようとしました。
public partial class SendMessagePage : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
string message = Request.QueryString["Message"];
string clientId = Request.QueryString["ClientId"];
AsyncMessageSender sender = new AsyncMessageSender(clientId, message);
sender.Start();
Response.Write("Success");
}
}
AsyncMessageSenderクラス:
public class AsyncMessageSender
{
private BackgroundWorker backgroundWorker;
private string client;
private string msg;
public AsyncMessageSender(string clientId, string message)
{
this.client = clientId;
this.msg = message;
// setup background thread to listen
backgroundThread = new BackgroundWorker();
backgroundThread.WorkerSupportsCancellation = true;
backgroundThread.DoWork += new DoWorkEventHandler(backgroundThread_DoWork);
}
public void Start()
{
backgroundThread.RunWorkerAsync();
}
...
// after that it's pretty predictable
}
ユーザーに何も返さなくてもよい場合は、別のスレッドを起動するか、デリゲートを使用して非同期ですばやくすばやくダーティなアプローチを呼び出します。非同期タスクの終了時にユーザーに通知する必要がない場合は、コールバックを無視できます。 SomeVeryLongAction()メソッドの最後にブレークポイントを配置してみてください。ページが既に提供された後に実行が終了することがわかります。
private delegate void DoStuff(); //delegate for the action
protected void Page_Load(object sender, EventArgs e)
{
}
protected void Button1_Click(object sender, EventArgs e)
{
//create the delegate
DoStuff myAction = new DoStuff(SomeVeryLongAction);
//invoke it asynchrnously, control passes to next statement
myAction.BeginInvoke(null, null);
Button1.Text = DateTime.Now.ToString();
}
private void SomeVeryLongAction()
{
for (int i = 0; i < 100; i++)
{
//simulation of some VERY long job
System.Threading.Thread.Sleep(100);
}
}
Webフォームを実行している場合、リクエストを行う.aspxページでAnsync = "true"を設定します。<%@ Page Language="C#" Async="true" ... %>
問題はここにあります:Async属性は、ページがスレッドをブロックする長時間実行タスクを呼び出す場合であり、ページはユーザーに情報を返すためにそのタスクからの出力を必要とします。たとえば、ページがWebサービスを呼び出す必要がある場合、その応答を待ってから、応答からのデータを使用してページをレンダリングします。
Async属性を使用する理由は、スレッドのブロックを回避するためです。 ASP.NETアプリケーションは要求を処理するためにスレッドプールを使用し、利用可能なスレッドの数は比較的少ないため、これは重要です。そして、各呼び出しがWebサービス呼び出しを待っている間にスレッドを結びつけると、すぐに十分な同時ユーザーにヒットし、ユーザーはこれらのWebサービス呼び出しが完了するまで待たなければなりません。 Async属性を使用すると、スレッドはスレッドプールに戻り、Webサイトへの同時訪問者にサービスを提供します。Webサービスの呼び出しが戻るのを待つ間、スレッドは何もせずに座っています。
結果は次のとおりです。非同期属性は、非同期タスクが完了するまでページをレンダリングできない場合のために設計されているため、すぐにページをレンダリングしません。
独自のスレッドを起動して、デーモンスレッドにする必要があります。その正確な構文は覚えていませんが、BCLのドキュメントで「デーモン」を検索することで、ドキュメント内で簡単に見つけることができます。これは、スレッドが、ASP.NETおよびIISが必要と判断したときに「プロセスをリサイクルする」権利を留保するため重要であるため、スレッドの動作中にそれが発生すると、タスクは停止します。スレッドデーモンを作成すると、これを防ぐことができます(まれに発生する可能性のあるEdgeのケースを除きます...これに関するドキュメントを見つけると、詳細がわかります)。
そのデーモンスレッドは、これらのタスクを開始する場所です。デーモンスレッドにタスクを実行するように指示した後、すぐにページをレンダリングできます...そのため、ページのレンダリングはすぐに行われます。
ただし、ASP.NETプロセスのデーモンスレッドよりも優れているのは、タスクを実行するためのWindowsサービスを実装することです。 ASP.NETアプリケーションに、実行するタスクをサービスに伝えます。デーモンスレッドは不要で、ASP.NETプロセスがリサイクルされることを心配する必要もありません。サービスにタスクを実行するように指示するにはどうすればよいですか?おそらく、WCFを介して、またはおそらくサービスがポーリングするデータベーステーブルにレコードを挿入することによって。または他のいくつかの方法。
編集:これは、これとまったく同じ目的で以前に使用した別のアイデアです。タスクに関する情報をMSMQキューに書き込みます。別のプロセス(別のマシン上であっても)にそのキューからプルさせ、時間のかかるタスクを実行させます。キューに挿入するジョブは、できるだけ早く戻るように最適化されているため、キューに入れたデータが回線などを介して送信されている間、スレッドはブロックされません。これは、タスクが実行されるのを待たずにタスクを実行する必要があるという事実をメモする最も速い方法の1つです。
Asyncをtrueに設定しなくても、この制限を非常に簡単に回避できます。
public void Start()
{
new Task(() =>
{
backgroundThread.RunWorkerAsync();
}).Start();
}
Webサービスを非同期で呼び出すときにこのエラーが発生する場合は、例外メッセージの指示に従ってAsync = 'true'属性を追加してください。
ページのトップ<ページLanguage = 'VB' Async = 'true' AutoEventWireup = 'false' CodeFile = 'mynewpage.aspx.vb' Inherits = 'mynewpage'%>