我有以下(简化)异步方法:
void Transform<X,Y>(X x, Action<Y> resultCallback) {...}
我想要做的是将X列表转换为Y列表。
问题在于,即使Transform方法是异步的,也必须以串行方式调用它(即我必须在使用下一个值调用它之前等待回调)。
有没有办法优雅地做到这一点? (我在.Net 4.0上)
我猜测继续传递可能有某种方法可以做到这一点......
更新我忘了指定我不想阻止调用(GUI)线程。
答案 0 :(得分: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线程完成后禁用“throbber”(来自注释)。
如果您控制所有这些代码,您可以从一开始就使您的转换过程同步,并将其移动到如上所述的后台线程中,从而避免使用包装器。
答案 1 :(得分:1)
正如我在我的问题中暗示的那样,我想知道使用延续传递的解决方案。以下扩展方法允许我使用相当“漂亮”的用法:
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
}
一个明显的限制是堆栈溢出 - 对于我的目的,这不会是一个问题(我在数十项,而不是数千)。请评论中任何其他明显的蠢货!