2009-01-02 16 views
4

Andreas Huber对this question的回答让我想到了使用异步委托实现Concurrent<T>而不是ThreadPool。但是,当AsyncCallback传递给BeginInvoke时,我发现很难理解发生了什么,特别是当多个线程可以访问IAsyncResult时。不幸的是,这种情况似乎没有涵盖在MSDN或任何我能找到的地方。而且,我能找到的所有文章都是在关闭和泛型可用之前编写的,或者看起来就是这样。有几个问题(我希望是真实的答案,但我准备好失望):以多线程方式使用BeginInvoke/EndInvoke。 AsyncCallback,AsyncWaitHandle和IsCompleted如何交互?

1)使用闭包作为AsyncCallback有什么区别?
(但愿不是)
2)如果在AsyncWaitHandle一个线程等待,将它标志着
一)回调开始或 B)为完成后过吗?
(希望b)
3)当回调正在运行时,IsCompleted会返回什么?可能性我可以看到:
a)true; b)false; c)false之前的回调调用EndInvoke,true之后。
(希望是b或c)
4)如果线程在调用EndInvoke之后等待AsyncWaitHandle会抛出?
(希望不是,但我希望是)。

提供的答案是,因为我希望,这似乎像它应该工作:

public class Concurrent<T> { 
    private IAsyncResult _asyncResult; 
    private T _result; 

    public Concurrent(Func<T> f) { // Assume f doesn't throw exceptions 
     _asyncResult = f.BeginInvoke(
          asyncResult => { 
           // Assume assignment of T is atomic 
           _result = f.EndInvoke(asyncResult); 
          }, null); 
    } 

    public T Result { 
     get { 
      if (!_asyncResult.IsCompleted) 
       // Is there a race condition here? 
       _asyncResult.AsyncWaitHandle.WaitOne(); 
      return _result; // Assume reading of T is atomic 
     } 
    ... 

如果问题的答案1-3都是我希望,应该在这里没有raace条件,据我所见。

回答

2

问题1

我认为问题的一部分是误解。 IAsyncResult不会从多个线程访问,除非您明确地将它传递给一个线程。如果您查看BCL中的开始***样式API的实现,您会注意到IAsyncResult只在实际发生Begin ***或End ***调用的线程中创建和销毁。操作已100%完成后

问题2

AsyncWaitHandle应该用信号。

问题3

一旦底层操作完成IsCompleted应返回true(没有更多的工作要做)。查看IsComplete最好的办法是,如果该值是

  1. 真实 - >调用结束***将立即返回
  2. 假 - > Callind结束***会阻塞一段时间

问题4

这是实现相关。这里没有办法真正给出一个全面的答案。

样品

如果你有兴趣的API,它允许完成后在另一个线程您可以轻松地运行一个委托和访问结果中,检查出我RantPack Utility Library。它以源代码和二进制形式提供。它具有完全充实的Future API,可以让代表同时运行。

此外还有一个IAsyncResult的实现,它涵盖了这篇文章中的大多数问题。

+0

尽管如果我想在这里实现IAsyncResult,那么您的答案将非常适用,但我不这样做。我在询问有关System.Runtime.Remoting.Messaging.AsyncResult的行为,这是BeginInvoke实际返回的行为。 – 2009-01-02 15:04:58

2

我一直在寻找最近的异步调用。我发现了一篇指向文章的指针,该文章由着名作家Jeffrey Richter编写的example implementation of an IAsyncResult。通过研究这个实现,我学到了很多关于异步调用是如何工作的。

您可能还会看到是否可以下载并检查您特别关注的System.Runtime.Remoting.Messaging.AsyncResult的源代码。这是一个link to instructions on how to do this in Visual Studio

要有点添加到JaredPar的很好的答案...

1:我相信如果你定义可分配给AsyncCallback类型的变量封闭(需要一个IAsyncResult,返回void),它应该作为工作你会期望一个闭包作为该委托来工作,但我不确定是否可能存在范围问题。原始的本地作用域应该在回调被调用之前返回很久(这就是使它成为异步操作的原因),因此请牢记关于本地(堆栈)变量的引用以及这些变量的行为方式。我想,引用成员变量应该没问题。

2:我从您的评论中想到,您可能误解了这个答案。在Jeffrey Richter的示例实现中,等待句柄在之前被标记为,该回调被调用。如果你想一想,就必须这样。一旦它调用回调,就会失去对执行的控制。假设回调方法抛出一个异常....执行可能会退回到调用回调的方法之后,从而阻止它稍后发出等待句柄的信号!所以等待句柄需要在回调被调用之前发出信号。如果按照该顺序执行,则它们的时间更接近,而不是仅在回调返回后才发出等待句柄。

3:正如JaredPar所说,IsCompleted应返回true之前的回调之前和等待手柄信号之前。这是有道理的,因为如果IsCompleted为false,您会期望EndInvoke的呼叫阻塞,并且等待句柄的整个点(与回调一样)是知道结果何时准备就绪,并且将不会块。所以,首先IsCompleted设置为true,然后等待手柄发信号,然后调用回调。看杰弗里·里希特如何做到这一点。但是,你可能应该尽量避免假设这三个方法(轮询,等待处理,回调)可能检测到完成的顺序,因为可能以不同于预期的顺序来实现它们。

4:除了可以通过调试到您感兴趣的实现的框架源代码找到答案外,我无法帮到您。或者你可能会想出一个实验来找出......或者设置一个好的实验并调试到框架源代码中,以确定它。

相关问题