2010-10-11 89 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