2010-10-11 91 views
2

我有以下的(簡化)異步方法:調用異步方法連續

void Transform<X,Y>(X x, Action<Y> resultCallback) {...} 

,我想要做的是改變x s的列表爲伊蘇名單。

問題是即使Transform方法是異步的,它也必須被串行調用(即,在用下一個值調用它之前,我必須等待回調)。

有什麼辦法可以優雅地做到這一點? (我在.NET 4.0)

我猜有可能是某種方式與延續傳遞做...

UPDATE我忘了指定,我不希望阻止調用(GUI)線程。

+0

您是否控制了作爲'resultCallback'傳遞的方法? – arootbeer 2010-10-11 18:55:10

+0

@arootbeer,是的。 – Benjol 2010-10-11 19:35:35

回答

4

如果你把這個包在一個輔助類,你可以做幫手 「同步」 你的價值觀:

public class AsyncWrapper<X,Y> 
{ 
    ManualResetEvent mre; 
    private Y result; 

    public Y Transform(X x, YourClass yourClass) 
    { 
     mre = new ManualResetEvent(false); 
     result = default(Y); 

     yourClass.Transform<X,Y>(x, this.OnComplete); 
     mre.WaitOne(); 
     return result; 
    } 

    void OnComplete(Y y) 
    { 
     result = y; 
     mre.Set(); 
    }   
} 

然後,您可以使用此類似:

// instance your class with the Transform operation 
YourClass yourClass = new YourClass(); 

AsyncWrapper<X,Y> wrapper = new AsyncWrapper<X,Y>(); 

foreach(X x in theXCollection) 
{ 
    Y result = wrapper.Transform(x, yourClass); 

    // Do something with result 
} 

編輯:

既然你說你正在試圖做到這一點,以保持一切在後臺線程上運行,你可以使用我的代碼上面,一個第二做:

// Start "throbber" 
Task.Factory.StartNew() => 
{ 
    // instance your class with the Transform operation 
    YourClass yourClass = new YourClass(); 

    AsyncWrapper<X,Y> wrapper = new AsyncWrapper<X,Y>(); 

    foreach(X x in theXCollection) 
    { 
     Y result = wrapper.Transform(x, yourClass); 

     // Do something with result 
    } 
}).ContinueWith(t => 
{ 
    // Stop Throbber 
}, TaskScheduler.FromCurrentSynchronizationContext()); 

一旦完成這將啓動在後臺線程整個(現在同步)過程,並禁用「活動指示器」(從評論)在UI線程上。

如果你控制了所有這些代碼,你可以使你的Transform過程從一開始就同步,並且像上面那樣將它移動到後臺線程中,從而避免了對包裝器的需要。

+0

感謝您的快速回答,我現在不在辦公室,但我明天會試試!只有一個問題 - 這個轉換在另一個線程上的原因是爲了讓'throbber'不斷轉動(winforms),WaitOne塊是不是? – Benjol 2010-10-11 19:06:25

+0

@Benjol:是的 - 根據定義,將其設爲同步會阻止您稱之爲同步的線程。你可以將你的整個進程移動到後臺線程,但(例如:在後臺線程上串行處理整個循環)... – 2010-10-11 19:12:32

+0

@Benjol:我爲此添加了另一節 - 希望能夠告訴你我的意思... – 2010-10-11 20:10:27

1

正如我暗示了我的問題,我想知道使用continuation-passing的解決方案。下面的擴展方法讓我有一個相當「漂亮」的用法:

public static class Extensions 
{ 
    //Using an asynchronous selector, calculate transform for 
    // input list and callback with result when finished 
    public static void AsyncSelect<TIn, TOut>(this List<TIn> list, 
       Action<TIn, Action<TOut>> selector, 
       Action<List<TOut>> callback) 
    { 
     var listOut = new List<TOut>(); 
     list.AsyncSelectImpl(listOut, selector, callback); 
    } 

    //internal implementation - hides the creation of the output list 
    private static void AsyncSelectImpl<TIn, TOut>(this List<TIn> listIn, 
       List<TOut> listOut, 
       Action<TIn, Action<TOut>> selector, 
       Action<List<TOut>> callback) 
    { 
     if(listIn.Count == 0) 
     { 
      callback(listOut); //finished (also if initial list was empty) 
     } 
     else 
     { 
      //get first item from list, recurse with rest of list 
      var first = listIn[0]; 
      var rest = listIn.Skip(1).ToList(); 
      selector(first, result => { 
          listOut.Add(result); 
          rest.AsyncSelectImpl(listOut, selector, callback); 
        }); 
     } 
    } 
} 

在主叫方,這導致:

(...) 
    //(For a Transform which performs int -> string) 
    Throbber.Start(); 
    inList.AsyncSelect<int,string>(Transform, WhenDone); 
} 
private void WhenDone(List<string> outList) 
{ 
    Throbber.Stop(); 
    //do something with outList 
} 

一個明顯的限制堆棧溢出 - 我的目的,即將不會是一個問題(我在幾十個項目,而不是數千)。請在評論中的任何其他明顯的bloopers!

+0

我試圖在IEnumerable(內置狀態機)上做這個簡單的調情,但我不認爲有可能做'順風'異步。 [Tomas Petricek](http://stackoverflow.com/users/33518/tomas-petricek)'upwind'async [here](http://tomasp.net/blog/csharp-async.aspx) – Benjol 2010-10-12 08:26:40