我有以下的(簡化)異步方法:調用異步方法連續
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