c#.net 3.5中的多线程异步web服务调用
我有两个ASP.net 3.5 asmx web服务,ws2和ws3。它们分别包含操作op21和op31。op21睡眠2秒,op31睡眠3秒。我想从web服务中的op11异步调用op21和op31,ws1。这样,当我从客户端同步调用op11时,所用的时间将是3秒,即总时间。我当前使用此代码获得5秒:c#.net 3.5中的多线程异步web服务调用,c#,.net,web-services,asynchronous,C#,.net,Web Services,Asynchronous,我有两个ASP.net 3.5 asmx web服务,ws2和ws3。它们分别包含操作op21和op31。op21睡眠2秒,op31睡眠3秒。我想从web服务中的op11异步调用op21和op31,ws1。这样,当我从客户端同步调用op11时,所用的时间将是3秒,即总时间。我当前使用此代码获得5秒: WS2SoapClient ws2 = new WS2SoapClient(); WS3SoapClient ws3 = new WS3SoapClient(); //capture time D
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);
预期结果是,两个请求的总时间应等于执行较慢请求所需的时间
请注意,当我使用VisualStudio调试它时,它会按预期工作,但是当在IIS上运行它时,时间是5秒,这似乎表明请求不会同时处理
我的问题是,是否需要正确设置IIS和ASMX web服务的特定配置,才能使其按预期工作?您的系统中存在一些限制。可能该服务仅为一个并发调用方配置,这是一个常见的原因(WCF ConcurrencyMode)。服务器上可能存在HTTP级别的连接限制(ServicePointManager.DefaultConnectionLimit)或WCF限制
使用Fiddler确定是否同时发送两个请求。使用调试器中断服务器,查看两个调用是否同时运行。原始答案: 我在google.com和bing.com上试过,我得到了同样的结果,线性执行。问题在于,您正在同一线程上启动BeginOP()调用,而AsyncResult(无论出于何种原因)在调用完成之前不会返回。有点没用 我的前TPL多线程有点生疏,但我在回答的最后测试了代码,它异步执行:这是一个.NET3.5控制台应用程序。请注意,我显然阻碍了您的一些代码,但使这些类看起来相同
更新:
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);
}
}
}
我开始猜测自己,因为我的执行时间如此接近,令人困惑。因此,我重新编写了一点测试,以使用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);
}
}
}
您在哪里实现WSxSoapClient?你用的是什么线?当我尝试运行您的代码(用Action.BeginInvoke替换您的web调用)时,我收到一个Stathrea不受支持的异常。您运行的是哪个版本的.net?使用TPL(.net 4或更高版本)可能是一种更优雅的方式,可读性也更高。@Aron它们是通过添加服务引用自动生成的SOAP客户端。该服务是使用c#构建的。下面是两个服务/操作,它们都已部署在localhost/IIS上。公共字符串OP31(){Thread.Sleep(3000);返回“OP31 Done”}公共字符串OP21(){Thread.Sleep(2000);返回“OP21 Done”}@DavidHaykin.NET 3.5您是否在本地计算机或服务器上测试此功能?您正在客户端的BeginOP31方法中调用sleep,这意味着它将以内联方式执行。这是正常的,因为异步IO不仅仅意味着在另一个线程上调用它(不涉及任何线程)。睡眠应该在服务器上。;我很困惑,为什么你能用谷歌和msn重新编程,它们肯定没有20秒左右的延迟…我在这两种情况下都调用Sleep(),这是为了模拟延迟。它内联执行,但在threadstart中,它在单独的线程上并发执行,因此总执行时间不会超过“较慢”线程的最大睡眠时间。我认为使用小型web请求是一种糟糕的测试方法。通常我使用TPL来完成这些事情,比如说,如果我在两个不同的DB服务器上运行一个存储过程需要3分钟,那么这段代码(使用我更喜欢的任务)将有助于运行它两次,但仍然只需要3分钟。问题是sleep方法不在服务器上,而是在soap客户端上。我的问题解决方案不正确。但是,我使用线程同步调用实现了这一点,调试时我得到3秒(较慢服务的总时间),从IIS运行时得到5秒(两个服务的总时间)。我需要进行任何配置吗?区别在于,异步只有在从不阻塞时才起作用。只有这样,它才能并发。真正的异步方法就是这样。没有线程正在启动以执行BeginXxx。真正的BeginXxx从来没有延迟。它不是wcf。这是一个ASP.NET web服务好的,您是否检查了其他节流可能性?你用过小提琴等吗?还没有,我对小提琴不熟悉。但是,当我调试应用程序时,它会按预期工作。我现在意识到这是一个iis问题,可能与配置或应用程序池有关,但我还不知道是什么问题。