等待等待另一个 IAsyncResult 的 IAsyncResult 方法(链接)

发布于 2024-10-30 01:39:56 字数 1770 浏览 7 评论 0原文

(只能使用 .NET 3.5 stock,所以没有任务,没有响应式扩展)

我有,我认为这是一个简单的案例,但我对此感到困惑。

简而言之,我将 BeginGetRequestStream 的 IAsyncResult 返回给 BeginMyOperation() 的调用者,并且我想真正发送回 BeginGetResponse 的 IAsyncResult,该 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);
        }

(can only use .NET 3.5 stock, so no Tasks, no 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.

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);
        }

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

没有伤那来痛 2024-11-06 01:39:56

我认为解决这个问题最简单的方法是使用 Task 包装器。特别是,您可以完成 TaskCompletionSourceBeginGetResponse 完成时。然后只需返回该 TaskCompletionSource< 的 Task /代码>。请注意,Task 实现了 IAsyncResult,因此您的客户端代码无需更改。

就个人而言,我会更进一步:

  1. BeginGetRequestStream 包装在 Task 中(使用 FromAsync)。
  2. 为该 Task 创建一个延续,用于处理请求并将 BeginGetResponse 包装在 Task 中(再次使用 FromAsync) 。
  3. 为第二个 Task 创建一个延续,以完成 TaskCompletionSource

恕我直言,TaskIAsyncResult 更自然地处理异常和结果值。

I think the easiest way to solve this is to use Task wrappers. In particular, you can finish a TaskCompletionSource when BeginGetResponse completes. Then just return the Task for that TaskCompletionSource. Note that Task implements IAsyncResult, so your client code won't have to change.

Personally, I would go a step further:

  1. Wrap BeginGetRequestStream in a Task (using FromAsync).
  2. Create a continuation for that Task that processes the request and wraps BeginGetResponse in a Task (again, using FromAsync).
  3. Create a continuation for that second Task that completes the TaskCompletionSource.

IMHO, exceptions and result values are more naturally handled by Tasks than IAsyncResult.

羁拥 2024-11-06 01:39:56

您尝试做的事情是可行的,但您需要创建 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
});

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).

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:

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
});
烟柳画桥 2024-11-06 01:39:56

我意识到这个问题已经有将近一年的历史了,但是如果提问者的限制仍然存在,.NET 3.5 上有一个选项可以轻松地编写异步操作。查看 Jeff Richter 的 PowerThreading 库。在 Wintellect.PowerThreading.AsyncProgModel 命名空间中,您将找到 AsyncEnumerator 类的多个变体,您可以将其与序列生成器一起使用来编写异步代码,就好像它是顺序代码一样。

其要点是,您将异步代码编写为返回 IEnumerator 的序列生成器的主体,并且每当您调用异步方法时,您都会发出 yield return code> 包含要等待的异步操作的数量。图书馆处理血淋淋的细节。

例如,要将一些数据发送到 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

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. Look at Jeff Richter's PowerThreading library. 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.

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. The library handles the gory details.

For example, to post some data to a url and return the contents of the result:

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. There are three async methods kicked off, as indicated by the three yield return 1 statements. With this pattern, you can chain as many async methods as you'd like and still just return one IAsyncResult to the caller.

悲歌长辞 2024-11-06 01:39:56

我不太明白你想要实现什么,但我认为你应该重新考虑代码。
IAsyncResult 实例是允许处理异步方法调用的对象,它们是在您通过BeginXXX 执行异步调用时创建的。

在您的示例中,您基本上希望返回 IAsyncResult 的一个实例,该实例尚不存在

我真的不知道您要解决的问题是什么,但也许这些方法之一更适合您:

  1. 封装此代码在一个类中,并使代码的用户意识到该操作通过订阅事件来完成。
  2. 将此代码封装在一个类中,并让用户提供一个回调委托,该回调委托将在工作完成时调用。您可以将结果作为参数传递给此回调

希望它有帮助!

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.

In your example, you basically want to return an instance of an IAsyncResult that it doesn't exist yet.

I don't really know which is the problem you are trying to solve, but maybe one of these approaches work better for you:

  1. Encapsulate this code in a class, and make the users of your code aware that the operation is completed by subscribing to an event.
  2. Encapsulate this code in a class, and make the users provide a callback delegate that will be called when the work is finished. You may pass the results as a parameter to this callback

Hope it helps!

滥情稳全场 2024-11-06 01:39:56

首先,从 Jeffrey Richter 的 MSDN 杂志文章“实现 CLR 异步编程模型(2007 年 3 月号)。"

一旦有了这些基类,您就可以相对轻松地实现自己的异步结果。在此示例中,我将使用您的基本代码来启动 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);
            }
        }
    }
}

需要注意的一些事项:

  • 您不能在异步回调的上下文中引发异常。您将使您的应用程序崩溃,因为没有人来处理它。相反,始终在出现异常的情况下完成异步操作。这保证了调用者将在 EndXxx 调用上看到异常,然后可以适当地处理它。
  • 假设无论 BeginXxx 可以抛出什么,也可以从 EndXxx 抛出。上面的示例假设 WebException 在任何一种情况下都可能发生。
  • 在调用者执行异步循环的情况下,设置“同步完成”状态非常重要。这将通知调用者何时需要从异步回调返回,以避免“堆栈潜水”。有关这方面的更多信息,请参阅 Michael Marucheck 的博客文章“Indigo 中的异步编程”(请参阅​​“Stack Dive”部分)。

异步编程并不是最简单的事情,但是一旦您理解了这些概念,它就会非常强大。

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)."

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.

// 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:

  • You cannot throw an exception in the context of an async callback. You will crash your application since there will be no one to handle it. Instead, always complete the async operation with an exception. This guarantees that the caller will see the exception on the EndXxx call and can then handle it appropriately.
  • Assume that whatever BeginXxx can throw is also possible to be thrown from EndXxx. The example above example assumes that WebException could happen in either case.
  • Setting the "completed synchronously" status is important in the case where a caller is doing an asynchronous loop. This will inform the caller when they need to return from their async callback in order to avoid "stack dives". More information on this is available here on Michael Marucheck's blog post "Asynchronous Programming in Indigo" (see the Stack Dive section).

Asynchronous programming is not the simplest thing but it is very powerful once you understand the concepts.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文