为了改善调用阻碍线程的长期运行的方法的XML Web服务方法的性能,你应该考虑把它们作为异步的XML Web服务方法发布。实现一个异步XML Web服务方法允许线程在返回线程池的时候执行其他的代码。这允许增加一个线程池中的有限数目的线程,这样提高了整体性能和系统的可伸缩性。
通常,调用执行输入/输出操作的方法的XML Web服务方法适于作为异步实现。这样的方法的例子包括和其他的XML Web服务通讯、访问远程数据库、执行网络输入/输出和读写大文件方法。这些方法都花费大量时间执行硬件级操作,而把线程留着用来执行XML Web服务方法程序块。如果XML Web服务方法异步实现,那么线程可以被释放来执行其他的代码。
不管一个XML Web服务方法是否异步实现,客户端都可以与之异步通讯。使用Web服务描述语言工具(WSDL.EXE)生成的.NET客户端中的代理类来实现异步通信,即使XML Web服务方法是同步实现。代理类包含用于与每个XML Web服务方法异步通信的Begin和End方法。因此,决定一个XML Web服务方法到底是异步还是同步要取决于性能。
注意:实现一个异步的XML Web服务方法对客户端和服务器上的XML Web服务之间的HTTP连接没有影响。HTTP连接既不不会关闭也不用连接池化。
实现一个异步的XML Web服务方法
实现一个异步的XML Web服务方法遵循NET Framework异步设计模式
把一个同步的XML Web服务方法分解为两个方法;其中每个都带有相同的基名--一个带有以Begin开头的名称,另一个带有以End开头的名称。
Begin方法的参数表包含方法的功能中的所有的in和by引用参数。
By引用参数是作为输入参数列出的。
倒数第二个参数必须是AsyncCallback。AsyncCallback参数允许客户端提供一个委托,在方法调用完成的时候调用。当一个异步XML Web服务方法调用另一个异步方法,这个参数可以被传入那个方法的倒数第二个参数。最后一个参数是一个对象。对象参数允许一个调用者提供状态信息给方法。当一个异步XML Web服务方法调用另一个异步方法,这个参数可以被传入那个方法的最后一个参数。
返回值必须是IAsyncResult类型的。
下面的代码示例是一个Begin方法,有一个方法函数特定的String参数。
[C#]
[WebMethod]
public IAsyncResult BeginGetAuthorRoyalties(String Author,
AsyncCallback callback, object asyncState)
[Visual Basic]
<WebMethod()> _
Public Function BeginGetAuthorRoyalties(ByVal Author As String, _
ByVal callback As AsyncCallback, ByVal asyncState As Object) _
As IAsyncResult
End方法的参数表由一个IAsyncResult类型的out和by引用参数组成。
返回值与一个同步的XML Web服务方法的返回值类型相同。
By引用参数是作为输出参数列出的。
下面的代码示例是一个End方法,返回一个AuthorRoyalties用户定义的模式。
[C#]
[WebMethod]
public AuthorRoyalties EndGetAuthorRoyalties(IAsyncResult
asyncResult)
[Visual Basic]
<WebMethod()> _
Public Function EndGetAuthorRoyalties(ByVal asyncResult As _
IAsyncResult) As AuthorRoyalties
下面的代码示例是一个和另一个XML Web服务方法异步通讯的异步XML Web服务方法。
[C#]
using System
using System.Web.Services
[WebService(Namespace="http://www.contoso.com/")]
public class MyService : WebService {
public RemoteService remoteService
public MyService() {
// Create a new instance of proxy class for
// the XML Web service to be called.
remoteService = new RemoteService()
}
// Define the Begin method.
[WebMethod]
public IAsyncResult BeginGetAuthorRoyalties(String Author,AsyncCallback callback, object asyncState) {
// Begin asynchronous communictation with a different XML Web
// service.
return remoteService.BeginReturnedStronglyTypedDS(Author,
callback,asyncState)
}
// Define the End method.
[WebMethod]
public AuthorRoyalties EndGetAuthorRoyalties(IAsyncResultasyncResult) {
// Return the asynchronous result from the other XML Web service.
return remoteService.EndReturnedStronglyTypedDS(asyncResult)
}
}
[Visual Basic]
Imports System.Web.Services
<WebService(Namespace:="http://www.contoso.com/")> _
Public Class MyService
Inherits WebService
Public remoteService As RemoteService
Public Sub New()
MyBase.New()
' Create a new instance of proxy class for
' the XML Web service to be called.
remoteService = New RemoteService()
End Sub
' Define the Begin method.
<WebMethod()> _
Public Function BeginGetAuthorRoyalties(ByVal Author As String, _
ByVal callback As AsyncCallback, ByVal asyncState As Object) _
As IAsyncResult
' Begin asynchronous communictation with a different XML Web
' service.
Return remoteService.BeginReturnedStronglyTypedDS(Author, _
callback, asyncState)
End Function
' Define the End method.
<WebMethod()> _
Public Function EndGetAuthorRoyalties(ByVal asyncResult As _
IAsyncResult) As AuthorRoyalties
' Return the asynchronous result from the other XML Web service.
Return remoteService.EndReturnedStronglyTypedDS(asyncResult)
End Function
End Class
下面的代码示例显示当一个XML Web服务方法产生了一个以上的异步调用并且这些调用必须连续执行时如何连接这些异步调用。BeginGetAuthorRoyalties方法产生一个异步调用用来判断传入的作者名是否有效,并设置一个名为AuthorRoyaltiesCallback的中间回调来接收结果。如果作者名有效,那么那个中间回调异步调用来获得作者的版税。
[C#]
using System.Web.Services
using System.Data
using System
// This imports the proxy class for the XML Web services
// that the sample communicates with.
using AsyncWS.localhost
namespace AsyncWS
{
[WebService(Namespace="http://www.contoso.com/")]
public class MyService : System.Web.Services.WebService
{
public RemoteService remoteService
public MyService()
{
remo teService = new RemoteService()
}
[WebMethod]
public IAsyncResult BeginGetAuthorRoyalties(String Author,
AsyncCallback callback, Object asyncState)
{
// Saves the current state for the call that gets the author's
// royalties.
AsyncStateChain state = new AsyncStateChain()
state.originalState = asyncState
state.Author = Author
state.originalCallback = callback
// Creates an intermediary callback.
AsyncCallback chainedCallback = new
AsyncCallback(AuthorRoyaltiesCallback)
return remoteService.BeginGetAuthors(chainedCallback,state)
}
// Intermediate method to handle chaining the
// asynchronous calls.
public void AuthorRoyaltiesCallback(IAsyncResult ar)
{
AsyncStateChain state = (AsyncStateChain)ar.AsyncState
RemoteService rs = new RemoteService()
// Gets the result from the call to GetAuthors.
Authors allAuthors = rs.EndGetAuthors(ar)
Boolean found = false
// Verifies that the requested author is valid.
int i = 0
DataRow row
while (i < allAuthors.authors.Rows.Count &&!found)
{
row = allAuthors.authors.Rows[i]
if (row["au_lname"].ToString() == state.Author)
{
found = true
}
i++
}
if (found)
{
AsyncCallback cb = state.originalCallback
// Calls the second XML Web service, because the author is
// valid.
rs.BeginReturnedStronglyTypedDS(state.Author,cb,state)
}
else
{
// Cannot throw the exception in this function or the XML Web
// service will hang. So, set the state argument to the
// exception and let the End method of the chained XML Web
// service check for it.
ArgumentException ex = new ArgumentException(
"Author does not exist.","Author")
AsyncCallback cb = state.originalCallback
// Call the second XML Web service, setting the state to an
// exception.
rs.BeginReturnedStronglyTypedDS(state.Author,cb,ex)
}
}
[WebMethod]
public AuthorRoyalties EndGetAuthorRoyalties(IAsyncResult asyncResult)
{
// Check whehter the first XML Web service threw an exception.
if (asyncResult.AsyncState is ArgumentException)
throw (ArgumentException) asyncResult.AsyncState
else
return remoteService.EndReturnedStronglyTypedDS(asyncResult)
}
}
// Class to wrap the callback and state for the intermediate
// asynchronous operation.
public class AsyncStateChain
{
public AsyncCallback originalCallback
public Object originalState
public String Author
}
}
[Visual Basic]
Imports System.Web.Services
Imports System.Data
Imports System
' This imports the proxy class for the XML Web services
' that the sample communicates with.
Imports AsyncWS_VB.localhost
Namespace AsyncWs
<WebService(Namespace:="http://www.contoso.com/")> _
Public Class MyService
Inherits WebService
Public remoteService As remoteService
Public Sub New()
MyBase.New()
remoteService = New localhost.RemoteService()
End Sub
' Defines the Begin method.
<WebMethod()> _
Public Function BeginGetAuthorRoyalties(ByVal Author As String, _
ByVal callback As AsyncCallback, ByVal asyncState As Object) _
As IAsyncResult
' Saves the current state for the call that gets the author's
' royalties.
Dim state As AsyncStateChain = New AsyncStateChain()
state.originalState = asyncState
state.Author = Author
state.originalCallback = callback
' Creates an intermediary callback.
Dim chainedCallback As AsyncCallback = New AsyncCallback( _
AddressOf AuthorRoyaltiesCallback)
' Begin asynchronous communictation with a different XML Web
' service.
Return remoteService.BeginGetAuthors(chainedCallback, state)
End Function
' Intermediate method to handle chaining the asynchronous calls.
Public Sub AuthorRoyaltiesCallback(ByVal ar As IAsyncResult)
Dim state As AsyncStateChain = CType(ar.AsyncState, _
AsyncStateChain)
Dim rs As RemoteService = New RemoteService()
' Gets the result from the call to GetAuthors.
Dim allAuthors As Authors = rs.EndGetAuthors(ar)
Dim found As Boolean = False
' Verifies that the requested author is valid.
Dim i As Integer = 0
Dim row As DataRow
While (i < allAuthors.authors.Rows.Count And (Not found))
row = allAuthors.authors.Rows(i)
If (row("au_lname").ToString() = state.Author) Then
found = True
End If
i = i + 1
End While
If (found) Then
Dim cb As AsyncCallback = state.originalCallback
' Calls the second XML Web service, because the author is
' valid.
rs.BeginReturnedStronglyTypedDS(state.Author, cb, state)
Else
' Cannot throw the exception in this function or the XML Web
' service will hang. So, set the state argument to the
' exception and let the End method of the chained XML Web
' service check for it.
Dim ex As ArgumentException = New ArgumentException( "Author does not exist.", "Author")
Dim cb As AsyncCallback = state.originalCallback
' Call the second XML Web service, setting the state to an
' exception.
rs.BeginReturnedStronglyTypedDS(state.Author, cb, ex)
End If
End Sub
' Define the End method.
<WebMethod()> _
Public Function EndGetAuthorRoyalties(ByVal asyncResult As _
IAsyncResult) As localhost.AuthorRoyalties
' Return the asynchronous result from the other XML Web service.
Return remoteService.EndReturnedStronglyTypedDS(asyncResult)
End Function
End Class
' Class to wrap the callback and state for the intermediate asynchronous
' operation.
Public Class AsyncStateChain
Public originalCallback As AsyncCallback
Public originalState As Object
Public Author As String
End Class
End Namespace
一.异步调用webservice方法:不管是Winform还是Web调用,都是可以对Webservice进行异步调用的。
方法有两种:
(1)Begin/End方法(虽然被丢弃)。
(2)事件驱动方法。
下面来具体谈谈这两种方法;
1、Begin/End方法
使用 Begin/End 模式实现进行异步方法调用的 Web 服务客户端
客户端如何知道何时调用 End 方法呢?根据 .NET Framework 的定义,有两种方法可以使客户端确定这一点:
客户端调用方式1等待方法:使用 WaitHandle 类的方法之一使客户端等待方法完成。
客户端调用方式2回调方法:向 Begin 方法传递一个回调函数,在该方法完成处理后再调用该函数来检索结果。
注意:无论客户端选择两种方法中的哪一种与 Web 服务进行异步通信,收发的 SOAP 消息都与通过同步代理方法生成的 SOAP 消息相同。也就是说,仍然只有一个 SOAP 请求和 SOAP 响应通过网络发送和接收。代理类通过使用与客户端调用 Begin 方法时使用的线程不同的线程来处理 SOAP 响应,从而实现该目的。因此,在代理类接收和处理 SOAP 响应时,客户端可以在其线程上继续执行其他工作。
webservice代码:
客户端调用方式1(Begin/End 模式的回调方法):
解说(解说来自MSDN):
<!--[if !supportLists]-->
定义实现 AsyncCallback 委托的回调函数。
public static void FactorizeCallback(IAsyncResult ar)
<!--[if !supportLists]-->
实例化 AsyncCallback 委托。
<!--[endif]-->
AsyncCallback cb = new AsyncCallback(TestCallback.FactorizeCallback)
<!--[if !supportLists]--><!--[endif]-->调用 Begin 方法,将回调函数作为第二个参数传递,将提供状态的对象(在本示例中是 PrimeFactorizer 的客户端实现)作为第三个参数传递。
IAsyncResult ar = pf.BeginFactorize(factorizableNum, cb, pf)
<!--[if !supportLists]--><!--[endif]-->在 Begin 方法返回的 IAsyncResult 上检查 IsCompleted 属性。如果客户端已收到服务器的响应,该值将设置为 true。
<!--[if !supportLists]--><!--[endif]-->在回调函数中访问状态对象。IAsyncState 参数的 AsyncState 属性将该对象作为第三个参数传递给 Begin 方法。
PrimeFactorizer pf = (PrimeFactorizer) ar.AsyncState
<!--[if !supportLists]--><!--[endif]-->在回调函数中,对上一步中获取的状态对象调用 End 方法。
long[] results = pf.EndFactorize(ar)
客户端调用方式2(Begin/End 模式的等待方法):
解说(解说来自MSDN):
WaitHandle 类实现下列支持等待同步对象得到信号通知的方法:WaitOne、WaitAny 和 WaitAll。当同步对象得到信号通知时,表示等待指定资源的线程此时可以访问该资源了。Web 服务客户端通过 Begin 方法返回的 IAsyncResult 对象的 AsyncWaitHandle 属性,来访问 WaitHandle 对象。
具体做法:
<!--[if !supportLists]-->1、 <!--[endif]-->Web 服务客户端调用所生成的代理类的 Begin 方法。
IAsyncResult ar = pf.BeginFactorize(factorizableNum, null, null)
<!--[if !supportLists]-->2、 <!--[endif]-->Web 服务客户端通过返回的 IAsyncResult 的 AsyncWaitHandle 属性来访问 WaitHandle 对象。
ar.AsyncWaitHandle.WaitOne()
<!--[if !supportLists]-->3、 <!--[endif]-->等待方法返回后,客户端调用 End 方法来获取结果。
results = pf.EndFactorize(ar)
2、事件驱动方法
解说:
Web 服务代理类中的每个 Web 方法都具有一个异步的对应版本。代理类自动生成每个 Web 方法的异步方法和对应事件。当异步方法被调用时,将在另一个线程上执行,并在返回时引发对应的事件。可以为异步方法的对应事件创建一个处理程序,从而 在返回时执行代码。
此模型可以简化生成多线程应用程序的过程,因为不必自己实现复杂的多线程代码。
具体做法:
<!--[if !supportLists]-->1、 <!--[endif]-->创建MethodCompleted 事件。
private void HelloWorldCompleted(Object sender,
localhost.HelloWorldCompletedEventArgs Completed)
{
// Insert code to implement the method here
}
<!--[if !supportLists]-->2、 <!--[endif]-->向该事件的处理程序列表添加 MethodCompleted 事件处理程序。
myWebService.HelloWorldCompleted += new localhost.HelloWorldCompletedEventHandler(HelloWorldCompleted)<!--[if !supportLists]-->3、 <!--[endif]-->使用该方法的 MethodAsync 形式调用 Web 方法.
HelloWorldAsync()
二.异步调用的优势:异步出来的新线程,.NET也是不允许的,所以别想钻空子,下面可以很容易想到,回收分为2种情况:主动回收和被动回收,主动回收就是,去监视那个线程,并且等待,当异步方法完成了,就把异步线程回收,焦点回归主线程,BeginInvoke之后又EndInvoke,如果在EndInvoke的时候,该异步线程没有完成操作,那么整个程序,包括主线程,又在阻塞了,又会出现界面的情况。要想解决这个问题,就使用“被动回收”方式,其中一个重要的办法就是“异步回调”。 核心有二: A、 用回调函数,异步结束后,自动调用此回调函数。 B、 而不在主线程中手工等待异步结束,如上两例中在主线程中调用EndInvoke。此种方法,是在回调函数中调用EndInvoke的。
欢迎分享,转载请注明来源:夏雨云
评论列表(0条)