我有以下的(简化)异步方法:调用异步方法连续
void Transform<X,Y>(X x, Action<Y> resultCallback) {...}
,我想要做的是改变x s的列表为伊苏名单。
问题是即使Transform方法是异步的,它也必须被串行调用(即,在用下一个值调用它之前,我必须等待回调)。
有什么办法可以优雅地做到这一点? (我在.NET 4.0)
我猜有可能是某种方式与延续传递做...
UPDATE我忘了指定,我不希望阻止调用(GUI)线程。
我有以下的(简化)异步方法:调用异步方法连续
void Transform<X,Y>(X x, Action<Y> resultCallback) {...}
,我想要做的是改变x s的列表为伊苏名单。
问题是即使Transform方法是异步的,它也必须被串行调用(即,在用下一个值调用它之前,我必须等待回调)。
有什么办法可以优雅地做到这一点? (我在.NET 4.0)
我猜有可能是某种方式与延续传递做...
UPDATE我忘了指定,我不希望阻止调用(GUI)线程。
如果你把这个包在一个辅助类,你可以做帮手 “同步” 你的价值观:
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过程从一开始就同步,并且像上面那样将它移动到后台线程中,从而避免了对包装器的需要。
感谢您的快速回答,我现在不在办公室,但我明天会试试!只有一个问题 - 这个转换在另一个线程上的原因是为了让'throbber'不断转动(winforms),WaitOne块是不是? – Benjol 2010-10-11 19:06:25
@Benjol:是的 - 根据定义,将其设为同步会阻止您称之为同步的线程。你可以将你的整个进程移动到后台线程,但(例如:在后台线程上串行处理整个循环)... – 2010-10-11 19:12:32
@Benjol:我为此添加了另一节 - 希望能够告诉你我的意思... – 2010-10-11 20:10:27
正如我暗示了我的问题,我想知道使用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!
我试图在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
您是否控制了作为'resultCallback'传递的方法? – arootbeer 2010-10-11 18:55:10
@arootbeer,是的。 – Benjol 2010-10-11 19:35:35