[英]Waiting on an IAsyncResult method that waits on another IAsyncResult (Chaining)
(只能使用.NET 3.5库存,所以没有任务,没有Reactive Extensions)
我有,我认为是一个简单的案例,但我很困惑。
缺点是,我将BeginGetRequestStream的IAsyncResult返回给BeginMyOperation()的调用者,我想真正发送回BeginGetResponse的IAsyncResult,它在调用EndGetRequestStream时调用。
所以我想知道,我该怎么做
public IAsyncResult BeginMyOperation(...)
{
HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(requestUri);
webRequest.Method = "POST";
// This is the part, that puzzles me. I don't want to send this IAsyncResult back.
return webRequest.BeginGetRequestStream(this.UploadingStreamCallback, state);
}
// Only want this to be called when the EndGetResponse is ready.
public void EndMyOperation(IAsyncResult ar)
{
}
private IAsyncResult UploadingStreamCallback(IAsyncResult asyncResult)
{
using (var s = state.WebRequest.EndGetRequestStream(asyncResult))
{
using (var r = new BinaryReader(state.Request.RequestData))
{
byte[] uploadBuffer = new byte[UploadBufferSize];
int bytesRead;
do
{
bytesRead = r.Read(uploadBuffer, 0, UploadBufferSize);
if (bytesRead > 0)
{
s.Write(uploadBuffer, 0, bytesRead);
}
}
while (bytesRead > 0);
}
}
// I really want to return this IAsyncResult to the caller of BeginMyOperation
return state.WebRequest.BeginGetResponse(new AsyncCallback(state.Callback), state);
}
我认为解决这个问题的最简单方法是使用Task
包装器。 特别是,您可以在BeginGetResponse
完成时完成TaskCompletionSource
。 然后只返回TaskCompletionSource
的Task
。 请注意, Task
实现了IAsyncResult
,因此您的客户端代码不必更改。
就个人而言,我会更进一步:
Task
包装BeginGetRequestStream
(使用FromAsync
)。 Task
创建一个继续,该Task
处理请求并在Task
包装BeginGetResponse
(同样,使用FromAsync
)。 TaskCompletionSource
第二个Task
创建一个延续。 恕我直言,异常和结果值更自然地处理Task
总比IAsyncResult
。
我意识到这个问题差不多有一年了,但如果提问者的约束仍然存在,.NET 3.5上有一个选项可以轻松地组成异步操作。 看看Jeff Richter的PowerThreading库 。 在Wintellect.PowerThreading.AsyncProgModel
命名空间中,您将找到AsyncEnumerator
类的几种变体,您可以将它们与序列生成器一起使用来编写异步代码,就好像它是顺序的一样。
它的要点是您将异步代码编写为返回IEnumerator<int>
的序列生成器的主体,并且每当调用异步方法时,您都会发出一个带有等待的异步操作数的yield return
。 图书馆处理血腥细节。
例如,要将一些数据发布到url并返回结果的内容:
public IAsyncResult BeginPostData(string url, string content, AsyncCallback callback, object state)
{
var ae = new AsyncEnumerator<string>();
return ae.BeginExecute(PostData(ae, url, content), callback, state);
}
public string EndPostData(IAsyncResult result)
{
var ae = AsyncEnumerator<string>.FromAsyncResult(result);
return ae.EndExecute(result);
}
private IEnumerator<int> PostData(AsyncEnumerator<string> ae, string url, string content)
{
var req = (HttpWebRequest)WebRequest.Create(url);
req.Method = "POST";
req.BeginGetRequestStream(ae.End(), null);
yield return 1;
using (var requestStream = req.EndGetRequestStream(ae.DequeAsyncResult()))
{
var bytes = Encoding.UTF8.GetBytes(content);
requestStream.BeginWrite(bytes, 0, bytes.Length, ae.end(), null);
yield return 1;
requestStream.EndWrite(ae.DequeueAsyncResult());
}
req.BeginGetResponse(ae.End(), null);
yield return 1;
using (var response = req.EndGetResponse(ae.DequeueAsyncResult()))
using (var responseStream = response.GetResponseStream())
using (var reader = new StreamReader(responseStream))
{
ae.Result = reader.ReadToEnd();
}
}
如您所见,私有PostData()
方法负责大部分工作。 有三种异步方法启动,如三个yield return 1
语句所示。 使用此模式,您可以根据需要链接尽可能多的异步方法,并且仍然只将一个IAsyncResult
返回给调用者。
你要做的事情是可行的,但你需要创建一个新的IAsyncResult实现(类似“CompositeResult”,它监视第一个IAsyncResult,然后启动第二个调用)。
但是,使用Reactive Extensions实际上这个任务实际上要容易得多 - 在这种情况下,您可以使用Observable.FromAsyncPattern将Begin / End方法转换为返回IObservable的Func( 也代表异步结果),然后使用SelectMany链接它们:
IObservable<Stream> GetRequestStream(string Url);
IObservable<bool> MyOperation(Stream stream);
GetRequestStream().SelectMany(x => MyOperation(x)).Subscribe(x => {
// When everything is finished, this code will run
});
我真的不明白你想要实现什么,但我认为你应该重新考虑代码。 IAsyncResult实例是允许处理异步方法调用的对象,它们是在通过BeginXXX执行异步调用时创建的。
在您的示例中,您基本上希望返回它尚不存在的IAsyncResult实例。
我真的不知道你要解决的问题是哪一个,但也许这些方法中的一种对你来说效果更好:
希望能帮助到你!
首先,从Jeffrey Richter的MSDN杂志文章“ 实现CLR异步编程模型 (2007年3月号)”中获取AsyncResultNoResult
和AsyncResult<TResult>
实现代码。
拥有这些基类后,您可以相对轻松地实现自己的异步结果。 在此示例中,我将使用您的基本代码来启动Web请求,然后将响应作为由多个内部异步操作组成的单个异步操作来获取。
// This is the class that implements the async operations that the caller will see
internal class MyClass
{
public MyClass() { /* . . . */ }
public IAsyncResult BeginMyOperation(Uri requestUri, AsyncCallback callback, object state)
{
return new MyOperationAsyncResult(this, requestUri, callback, state);
}
public WebResponse EndMyOperation(IAsyncResult result)
{
MyOperationAsyncResult asyncResult = (MyOperationAsyncResult)result;
return asyncResult.EndInvoke();
}
private sealed class MyOperationAsyncResult : AsyncResult<WebResponse>
{
private readonly MyClass parent;
private readonly HttpWebRequest webRequest;
private bool everCompletedAsync;
public MyOperationAsyncResult(MyClass parent, Uri requestUri, AsyncCallback callback, object state)
: base(callback, state)
{
// Occasionally it is necessary to access the outer class instance from this inner
// async result class. This also ensures that the async result instance is rooted
// to the parent and doesn't get garbage collected unexpectedly.
this.parent = parent;
// Start first async operation here
this.webRequest = WebRequest.Create(requestUri);
this.webRequest.Method = "POST";
this.webRequest.BeginGetRequestStream(this.OnGetRequestStreamComplete, null);
}
private void SetCompletionStatus(IAsyncResult result)
{
// Check to see if we did not complete sync. If any async operation in
// the chain completed asynchronously, it means we had to do a thread switch
// and the callback is being invoked outside the starting thread.
if (!result.CompletedSynchronously)
{
this.everCompletedAsync = true;
}
}
private void OnGetRequestStreamComplete(IAsyncResult result)
{
this.SetCompletionStatus(result);
Stream requestStream = null;
try
{
stream = this.webRequest.EndGetRequestStream(result);
}
catch (WebException e)
{
// Cannot let exception bubble up here as we are on a callback thread;
// in this case, complete the entire async result with an exception so
// that the caller gets it back when they call EndXxx.
this.SetAsCompleted(e, !this.everCompletedAsync);
}
if (requestStream != null)
{
this.WriteToRequestStream();
this.StartGetResponse();
}
}
private void WriteToRequestStream(Stream requestStream) { /* omitted */ }
private void StartGetResponse()
{
try
{
this.webRequest.BeginGetResponse(this.OnGetResponseComplete, null);
}
catch (WebException e)
{
// As above, we cannot let this exception bubble up
this.SetAsCompleted(e, !this.everCompletedAsync);
}
}
private void OnGetResponseComplete(IAsyncResult result)
{
this.SetCompletionStatus(result);
try
{
WebResponse response = this.webRequest.EndGetResponse(result);
// At this point, we can complete the whole operation which
// will invoke the callback passed in at the very beginning
// in the constructor.
this.SetAsCompleted(response, !this.everCompletedAsync);
}
catch (WebException e)
{
// As above, we cannot let this exception bubble up
this.SetAsCompleted(e, !this.everCompletedAsync);
}
}
}
}
有些事情需要注意:
异步编程并不是最简单的事情,但一旦理解了概念,它就会非常强大。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.