1

非同期 POST を Web サービスに送信する必要があり、データを非同期で送信/取得したいと考えています。

また、リクエストを送信する必要がありますが、応答を待つのは最大 1500 ミリ秒だけです。応答が得られない場合、プログラムは続行する必要があります (これは、外部 Web サービス呼び出しを行うサービスです)。これらのサービス呼び出しを、長時間ブロックして返されるのを待つのではなく、IOCP にオフロードしたいと考えています。合計 1500 ミリ秒だけブロックしたい。

これが私がこれまでに持っているものです:

 var httpRequest = (HttpWebRequest)WebRequest.Create(@"urltoPostTo");

            httpRequest.Method = "POST";
            byte[] data = Encoding.ASCII.GetBytes("test-post");
            httpRequest.ContentLength = data.Length;
            var asyncTask = Task.Factory.FromAsync<Stream>(httpRequest.BeginGetRequestStream, httpRequest.EndGetRequestStream, httpRequest)
                .ContinueWith(response =>
                {
                    var localStream = response.Result;
                    if (localStream != null)
                    {
                        localStream.Write(data, 0, data.Length);
                        localStream.Close();
                    }
                });  //how do I do the continuation for BeginGetResponse and EndGetResponse from here?

残念ながら変更できない要件がいくつかあります。

  1. 4.0 を対象とする Visual Studio 2010 を使用しています
  2. 非同期 BCL を使用できません
  3. Task.FromAsync を試してみたい
4

3 に答える 3

1

これは、タイムアウトと CancellationTokens を適切に処理する拡張メソッドを使用して、CancellationToken をサポートする拡張メソッド WebRequest.GetResponseAsync の実装で既に回答されています。GetResponseAsyncRequest.Timeout が期限切れになると、メソッドはRequest.AbortTask をキャンセル状態で返す前に呼び出します。

このような複雑なコーディングの理由は、タイムアウトを適切に処理するのはクライアント (ユーザー) の責任であるため、タイムアウトの期限切れを処理するために FromAsync に依存することはできません。おそらくこれが、FromAsync がキャンセル トークンを受け入れない理由です。

もう 1 つのオプションは、リクエスト自体のキャンセルを回避し、継続をキャンセルすることです。CancellationToken を受け入れるContinueWithオーバーロードを使用し、 CancellationTokenSource.CancelAfterを呼び出してキャンセル タイムアウトを設定できます。

これにより、コードは結果を無視して実行を続けることができますが、サーバーへの接続が切断されることはなく、バックグラウンド スレッドが潜在的に高価な結果を処理するのを停止することもありません。

次のように書くことができます。

var tcs=new CancellationTokenSource();
var asyncTask = Task.Factory.FromAsync<Stream>(httpRequest.BeginGetRequestStream, httpRequest.EndGetRequestStream, httpRequest)
.ContinueWith(response =>
{...},
cts.Token);  
cts.CancelAfter(1500);

CancelAfter の呼び出しは、非同期タスクの開始後に行われることに注意してください。

ビジーなサイトでは Reed Copsey の拡張メソッドをお勧めします。キャンセルされたが未処理のリクエストが多数あると、スレッド プールが簡単に使い果たされ、理由もなく大量のメモリが消費され、外部システムへの潜在的に高価な接続が消費される可能性があるからです。

于 2013-11-11T09:05:44.143 に答える
0

このメソッド ヘルパーを試してください。

public static Task<string> Post(string url, Encoding encoding, string content)
            {
                var httpRequest = (HttpWebRequest)WebRequest.Create(url);
                httpRequest.Method = "POST";
                byte[] data = encoding.GetBytes(content);
                httpRequest.ContentLength = data.Length;

                TaskCompletionSource<string> result = new TaskCompletionSource<string>();


                Task.Factory.FromAsync<Stream>(httpRequest.BeginGetRequestStream, httpRequest.EndGetRequestStream, httpRequest)
                   .ContinueWith(requestStreamTask =>
                   {
                       try
                       {
                           using (var localStream = requestStreamTask.Result)
                           {
                               localStream.Write(data, 0, data.Length);
                               localStream.Flush();
                           }

                           Task.Factory.FromAsync<WebResponse>(httpRequest.BeginGetResponse, httpRequest.EndGetResponse, httpRequest)
                               .ContinueWith(responseTask =>
                               {
                                   try
                                   {
                                       using (var webResponse = responseTask.Result)
                                       using (var responseStream = webResponse.GetResponseStream())
                                       using (var sr = new StreamReader(responseStream, encoding))
                                       {
                                           result.SetResult(sr.ReadToEnd());
                                       }
                                   }
                                   catch (Exception e)
                                   {
                                       result.SetException(e);
                                   }
                               }, TaskContinuationOptions.AttachedToParent);
                       }
                       catch (Exception e)
                       {
                           result.SetException(e);
                       }

                   }, TaskContinuationOptions.AttachedToParent);

                return result.Task;
            }
于 2013-11-12T09:55:54.480 に答える