2

私は 2 つの ASP.net 3.5 asmx Web サービスws2ws3を持っています。これらには、それぞれ操作op21op31が含まれています。op21 は2 秒間スリープし、op31 は3 秒間スリープします。Web サービス ws1 のop11からop21と op31 の両方を非同期で呼び出したいと考えています。クライアントからop11を同期的に呼び出すと、所要時間は合計で3 秒になります。現在、次のコードで5 秒かかります。

WS2SoapClient ws2 = new WS2SoapClient();
WS3SoapClient ws3 = new WS3SoapClient();

//capture time
DateTime now = DateTime.Now;            
//make calls

IAsyncResult result1 = ws3.BeginOP31(null,null);
IAsyncResult result2 = ws2.BeginOP21(null,null);
WaitHandle[] handles = { result1.AsyncWaitHandle, result2.AsyncWaitHandle };

WaitHandle.WaitAll(handles);

//calculate time difference
TimeSpan ts = DateTime.Now.Subtract(now);
return "Asynchronous Execution Time (h:m:s:ms): " + String.Format("{0}:{1}:{2}:{3}",
ts.Hours,
ts.Minutes,
ts.Seconds,
ts.Milliseconds);

予想される結果は、両方のリクエストの合計時間が、遅い方のリクエストの実行にかかる時間と等しくなるはずです。

これは、Visual Studio でデバッグすると期待どおりに動作しますが、IIS でこれを実行すると、要求が同時に処理されていないことを示すように見える時間は 5 秒であることに注意してください。

私の質問は、これが期待どおりに機能するために適切にセットアップする必要がある IIS と ASMX Web サービスの特定の構成はありますか?

4

2 に答える 2

1

元の回答:

私は google.com でこれを試しましたが、bing.com は同じこと、線形実行を取得しています。問題は、同じスレッドで BeginOP() 呼び出しを開始していて、呼び出しが完了するまで (何らかの理由で) AsyncResult が返されないことです。役に立たない。

私のTPL前のマルチスレッドは少し錆びていますが、この回答の最後にあるコードをテストし、非同期で実行しました:これは.net 3.5コンソールアプリです。私は明らかにあなたのコードの一部を妨害しましたが、クラスを同じように見せました.


アップデート:

実行時間が互いに非常に近く、混乱していたので、私は自分自身を推測し始めました。そのため、テストを少し書き直して、元のコードと Thread.Start() を使用して提案したコードの両方を含めました。さらに、WebRequest メソッドに Thread.Sleep(N) を追加して、リクエストのさまざまな実行時間をシミュレートできるようにしました。

テスト結果は、投稿したコードが最初の回答で述べたように順次実行されたことを示しています。

試験結果

どちらの場合も、Thread.Sleep() のため、実際の Web 要求時間よりも合計時間がはるかに長いことに注意してください。また、Thread.Sleep() を追加して、サイトへの最初の Web リクエストがスピンアップするのに長い時間 (9 秒) かかるという事実を相殺しました (上記を参照)。いずれにせよ、「古い」ケースでは時間がシーケンシャルであり、新しいケースでは真に「非同期」であることは明らかです。


これをテストするための更新されたプログラム:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;

namespace MultiThreadedTest
{
    class Program
    {
        static void Main(string[] args)
        {
            // Test both ways of executing IAsyncResult web calls

            ExecuteUsingWaitHandles();
            Console.WriteLine();

            ExecuteUsingThreadStart();
            Console.ReadKey();
        }

        private static void ExecuteUsingWaitHandles()
        {
            Console.WriteLine("Starting to execute using wait handles (old way) ");

            WS2SoapClient ws2 = new WS2SoapClient();
            WS3SoapClient ws3 = new WS3SoapClient();

            IAsyncResult result1 = null;
            IAsyncResult result2 = null;

            // Time the threadas
            var stopWatchBoth = System.Diagnostics.Stopwatch.StartNew();
            result1 = ws3.BeginOP31();
            result2 = ws2.BeginOP21();

            WaitHandle[] handles = { result1.AsyncWaitHandle, result2.AsyncWaitHandle };
            WaitHandle.WaitAll(handles);

            stopWatchBoth.Stop();

            // Display execution time of individual calls
            Console.WriteLine((result1.AsyncState as StateObject));
            Console.WriteLine((result2.AsyncState as StateObject));

            // Display time for both calls together
            Console.WriteLine("Asynchronous Execution Time for both is {0}", stopWatchBoth.Elapsed.TotalSeconds);
        }

        private static void ExecuteUsingThreadStart()
        {
            Console.WriteLine("Starting to execute using thread start (new way) ");

            WS2SoapClient ws2 = new WS2SoapClient();
            WS3SoapClient ws3 = new WS3SoapClient();

            IAsyncResult result1 = null;
            IAsyncResult result2 = null;

            // Create threads to execute the methods asynchronously
            Thread startOp3 = new Thread( () => result1 = ws3.BeginOP31() );
            Thread startOp2 = new Thread( () => result2 = ws2.BeginOP21() );

            // Time the threadas
            var stopWatchBoth = System.Diagnostics.Stopwatch.StartNew();

            // Start the threads
            startOp2.Start();
            startOp3.Start();

            // Make this thread wait until both of those threads are complete
            startOp2.Join();
            startOp3.Join();

            stopWatchBoth.Stop();

            // Display execution time of individual calls
            Console.WriteLine((result1.AsyncState as StateObject));
            Console.WriteLine((result2.AsyncState as StateObject));

            // Display time for both calls together
            Console.WriteLine("Asynchronous Execution Time for both is {0}", stopWatchBoth.Elapsed.TotalSeconds);
        }
    }

    // Class representing your WS2 client
    internal class WS2SoapClient : TestWebRequestAsyncBase
    {
        public WS2SoapClient() : base("http://www.msn.com/") { }

        public IAsyncResult BeginOP21()
        {
            Thread.Sleep(TimeSpan.FromSeconds(10D));
            return BeginWebRequest();
        }
    }

    // Class representing your WS3 client
    internal class WS3SoapClient : TestWebRequestAsyncBase
    {
        public WS3SoapClient() : base("http://www.google.com/") { }

        public IAsyncResult BeginOP31()
        {
            // Added sleep here to simulate a much longer request, which should make it obvious if the times are overlapping or sequential
            Thread.Sleep(TimeSpan.FromSeconds(20D)); 
            return BeginWebRequest();
        }
    }

    // Base class that makes the web request
    internal abstract class TestWebRequestAsyncBase
    {
        public StateObject AsyncStateObject;
        protected string UriToCall;

        public TestWebRequestAsyncBase(string uri)
        {
            AsyncStateObject = new StateObject()
            {
                UriToCall = uri
            };

            this.UriToCall = uri;
        }

        protected IAsyncResult BeginWebRequest()
        {
            WebRequest request =
               WebRequest.Create(this.UriToCall);

            AsyncCallback callBack = new AsyncCallback(onCompleted);

            AsyncStateObject.WebRequest = request;
            AsyncStateObject.Stopwatch = System.Diagnostics.Stopwatch.StartNew();

            return request.BeginGetResponse(callBack, AsyncStateObject);
        }

        void onCompleted(IAsyncResult result)
        {
            this.AsyncStateObject = (StateObject)result.AsyncState;
            this.AsyncStateObject.Stopwatch.Stop();

            var webResponse = this.AsyncStateObject.WebRequest.EndGetResponse(result);
            Console.WriteLine(webResponse.ContentType, webResponse.ResponseUri);
        }
    }

    // Keep stopwatch on state object for illustration of individual execution time
    internal class StateObject
    {
        public System.Diagnostics.Stopwatch Stopwatch { get; set; }
        public WebRequest WebRequest { get; set; }
        public string UriToCall;

        public override string ToString()
        {
            return string.Format("Request to {0} executed in {1} seconds", this.UriToCall, Stopwatch.Elapsed.TotalSeconds);
        }
    }
}
于 2013-03-12T22:03:45.707 に答える
0

システムにいくつかのスロットルがあります。おそらく、サービスは、一般的な理由(WCF ConcurrencyMode)である1つの同時呼び出し元に対してのみ構成されています。サーバーにHTTPレベルの接続制限(ServicePointManager.DefaultConnectionLimit)またはWCFスロットリングがある可能性があります。

Fiddlerを使用して、両方のリクエストが同時に送信されているかどうかを確認します。デバッガーを使用してサーバーを中断し、両方の呼び出しが同時に実行されているかどうかを確認します。

于 2013-03-12T22:00:09.743 に答える