[英]Waiting on an IAsyncResult method that waits on another IAsyncResult (Chaining)
(can only use .NET 3.5 stock, so no Tasks, no Reactive Extensions) (只能使用.NET 3.5库存,所以没有任务,没有Reactive Extensions)
I have, what I thought to be a simple case, but I'm baffled at it. 我有,我认为是一个简单的案例,但我很困惑。
The short of it is that, I'm returning BeginGetRequestStream's IAsyncResult to the caller of BeginMyOperation(), and I want to really send back the IAsyncResult of BeginGetResponse, which is called when the EndGetRequestStream is called. 缺点是,我将BeginGetRequestStream的IAsyncResult返回给BeginMyOperation()的调用者,我想真正发送回BeginGetResponse的IAsyncResult,它在调用EndGetRequestStream时调用。
So I'm wondering, how do I 所以我想知道,我该怎么做
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);
}
I think the easiest way to solve this is to use Task
wrappers. 我认为解决这个问题的最简单方法是使用
Task
包装器。 In particular, you can finish a TaskCompletionSource
when BeginGetResponse
completes. 特别是,您可以在
BeginGetResponse
完成时完成TaskCompletionSource
。 Then just return the Task
for that TaskCompletionSource
. 然后只返回
TaskCompletionSource
的Task
。 Note that Task
implements IAsyncResult
, so your client code won't have to change. 请注意,
Task
实现了IAsyncResult
,因此您的客户端代码不必更改。
Personally, I would go a step further: 就个人而言,我会更进一步:
BeginGetRequestStream
in a Task
(using FromAsync
). Task
包装BeginGetRequestStream
(使用FromAsync
)。 Task
that processes the request and wraps BeginGetResponse
in a Task
(again, using FromAsync
). Task
创建一个继续,该Task
处理请求并在Task
包装BeginGetResponse
(同样,使用FromAsync
)。 Task
that completes the TaskCompletionSource
. TaskCompletionSource
第二个Task
创建一个延续。 IMHO, exceptions and result values are more naturally handled by Task
s than IAsyncResult
. 恕我直言,异常和结果值更自然地处理
Task
总比IAsyncResult
。
I realize that this question is almost one year old, but if the constraints of the asker still hold, there is an option available on .NET 3.5 to easily compose asynchronous operations. 我意识到这个问题差不多有一年了,但如果提问者的约束仍然存在,.NET 3.5上有一个选项可以轻松地组成异步操作。 Look at Jeff Richter's PowerThreading library .
看看Jeff Richter的PowerThreading库 。 In the
Wintellect.PowerThreading.AsyncProgModel
namespace, you will find several variants of the AsyncEnumerator
class, which you can use with sequence generators to write async code as if it were sequential. 在
Wintellect.PowerThreading.AsyncProgModel
命名空间中,您将找到AsyncEnumerator
类的几种变体,您可以将它们与序列生成器一起使用来编写异步代码,就好像它是顺序的一样。
The gist of it is that you write your async code as the body of a sequence generator that returns an IEnumerator<int>
, and whenever you call an async method you issue a yield return
with the number of async operations to wait for. 它的要点是您将异步代码编写为返回
IEnumerator<int>
的序列生成器的主体,并且每当调用异步方法时,您都会发出一个带有等待的异步操作数的yield return
。 The library handles the gory details. 图书馆处理血腥细节。
For example, to post some data to a url and return the contents of the result: 例如,要将一些数据发布到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();
}
}
As you can see, the private PostData()
method is responsible for the bulk of the work. 如您所见,私有
PostData()
方法负责大部分工作。 There are three async methods kicked off, as indicated by the three yield return 1
statements. 有三种异步方法启动,如三个
yield return 1
语句所示。 With this pattern, you can chain as many async methods as you'd like and still just return one IAsyncResult
to the caller. 使用此模式,您可以根据需要链接尽可能多的异步方法,并且仍然只将一个
IAsyncResult
返回给调用者。
The thing you're trying to do is doable, but you need to create a new implementation of IAsyncResult (something like "CompositeResult" that watches the first IAsyncResult, then kicks off the 2nd call). 你要做的事情是可行的,但你需要创建一个新的IAsyncResult实现(类似“CompositeResult”,它监视第一个IAsyncResult,然后启动第二个调用)。
However, this task is actually far easier using the Reactive Extensions - in that case you'd use Observable.FromAsyncPattern to convert your Begin/End methods into a Func that returns IObservable (which also represents an async result), then chain them using SelectMany: 但是,使用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
});
I don't really understand what are you trying to achieve, but I think you should be rethinking the code. 我真的不明白你想要实现什么,但我认为你应该重新考虑代码。 An IAsyncResult instance is the object that allows to to handle asynchronous method calls, and they are created when you perform an async call through BeginXXX .
IAsyncResult实例是允许处理异步方法调用的对象,它们是在通过BeginXXX执行异步调用时创建的。
In your example, you basically want to return an instance of an IAsyncResult that it doesn't exist yet . 在您的示例中,您基本上希望返回它尚不存在的IAsyncResult实例。
I don't really know which is the problem you are trying to solve, but maybe one of these approaches work better for you: 我真的不知道你要解决的问题是哪一个,但也许这些方法中的一种对你来说效果更好:
Hope it helps! 希望能帮助到你!
First, get the AsyncResultNoResult
and AsyncResult<TResult>
implementation code from Jeffrey Richter's MSDN magazine article " Implementing the CLR Asynchronous Programming Model (March 2007 issue)." 首先,从Jeffrey Richter的MSDN杂志文章“ 实现CLR异步编程模型 (2007年3月号)”中获取
AsyncResultNoResult
和AsyncResult<TResult>
实现代码。
Once you have those base classes, you can relatively easily implement your own async result. 拥有这些基类后,您可以相对轻松地实现自己的异步结果。 In this example, I will use your basic code to start the web request and then get the response as a single async operation composed of multiple inner async operations.
在此示例中,我将使用您的基本代码来启动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);
}
}
}
}
Some things to note: 有些事情需要注意:
Asynchronous programming is not the simplest thing but it is very powerful once you understand the concepts. 异步编程并不是最简单的事情,但一旦理解了概念,它就会非常强大。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.