串行调用异步方法

我有以下(简化)异步方法:

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

我想要做的是将X列表转换为Y列表。

问题是即使Transform方法是异步的,它也必须被串行调用(即我必须在使用下一个值调用它之前等待回调)。

有没有办法优雅地做到这一点? (我在.Net 4.0上)

我猜可能有一些方法可以继续传递……

更新我忘了指定我不想阻止调用(GUI)线程。

如果将它包装在辅助类中,则可以使帮助程序“同步”您的值:

 public class AsyncWrapper { ManualResetEvent mre; private Y result; public Y Transform(X x, YourClass yourClass) { mre = new ManualResetEvent(false); result = default(Y); yourClass.Transform(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 wrapper = new AsyncWrapper(); 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 wrapper = new AsyncWrapper(); foreach(X x in theXCollection) { Y result = wrapper.Transform(x, yourClass); // Do something with result } }).ContinueWith( t => { // Stop Throbber }, TaskScheduler.FromCurrentSynchronizationContext()); 

这将在后台线程上启动整个(现在是同步的)进程,并在UI线程完成后禁用“throbber”(来自注释)。

如果您控制所有这些代码,您可以从一开始就使您的转换过程同步,并将其移动到如上所述的后台线程中,从而避免使用包装器。

正如我在我的问题中暗示的那样,我想知道使用延续传递的解决方案。 以下扩展方法允许我使用相当“漂亮”的用法:

 public static class Extensions { //Using an asynchronous selector, calculate transform for // input list and callback with result when finished public static void AsyncSelect(this List list, Action> selector, Action> callback) { var listOut = new List(); list.AsyncSelectImpl(listOut, selector, callback); } //internal implementation - hides the creation of the output list private static void AsyncSelectImpl(this List listIn, List listOut, Action> selector, Action> 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(Transform, WhenDone); } private void WhenDone(List outList) { Throbber.Stop(); //do something with outList } 

一个明显的限制是堆栈溢出 – 对于我的目的,这不会是一个问题(我在数十项,而不是数千)。 请评论中任何其他明显的蠢货!