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)。服务器上可能存在 HTTP 级别的连接限制 (ServicePointManager.DefaultConnectionLimit) 或 WCF 限制。

使用 Fiddler 确定两个请求是否同时发送。使用调试器中断服务器并查看两个调用是否同时运行。

于 2013-03-12T22:00:09.743 回答