2009-01-02 42 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:除了可以通過調試到您感興趣的實現的框架源代碼找到答案外,我無法幫到您。或者你可能會想出一個實驗來找出......或者設置一個好的實驗並調試到框架源代碼中,以確定它。

相關問題