我有一个目前具有以下结构的应用程序
foreach (var req in requests)
{
var myReq = req;
var t = Task.Factory.StartNew(() =>
{
var result = valueService.Calc(myReq);
return result;
}
taskArray.Add(t)
t.Start();
}
Task.WaitAll(taskArray);
在我的应用程序中,我在每项任务中使用t.Result。
现在这很有效,因为我的Calc(myReq)
是一种同步方法。
但是,现在我已经被要求我的valuationService.Calc(myReq)
将调用异步方法,结果将以Callback方法返回。
class ValuationService
{
....
public MyResult Calc(MyRequest myReq)
{
obj.Callback = MyCallback;
obj.CalcAsync()
}
public void Callback(MyResult result)
{
...
}
所以我想要实现的是:让valuService.Calc(MyReq)保持其签名并以某种方式阻塞,直到运行回调方法。然后将回调的结果传递回Calc方法,以便它可以传回数据。
答案 0 :(得分:2)
所以我想要实现的是:让valuService.Calc(MyReq)保持其签名并以某种方式阻塞,直到运行回调方法。
如果那是你真正想要的,那么你可以用一个简单的ManualResetEvent
来做到这一点:
public MyResult Calc(MyRequest myReq)
{
var mre = new ManualResetEvent(false);
obj.Callback = result => MyCallback(result, mre);
obj.CalcAsync();
mre.WaitOne();
}
public void Callback(MyResult result, ManualResetEvent mre)
{
try
{
...
}
finally
{
mre.Set();
}
}
但是,请注意,现在代码将使用线程池线程只是为了处于等待状态,直到调用回调。这可能会影响您的表现。
另一种解决方案是接受异步工作的性质(即,不要在异步操作上浪费线程阻塞)。如果您同时具有CPU绑定和异步工作,则可以将TPL Dataflow视为一种解决方案。
答案 1 :(得分:0)
对于await
来说,这实际上是微不足道的:
async Task DoAsync(Action callback)
{
await WhateverAsync().ConfigureAwait(false);
callback();
}
你会想要确保回调中没有例外 - 因为从未等待任务,它们将被吞噬。根据需要添加自定义错误处理。
如果您没有await
可用,则可以使用延续执行类似的操作:
WhateverAsync().ContinueWith(t => callback());
它只是使错误处理有点棘手:)
最终结果是你有Task
可以在需要时同步等待 - 这似乎正是你想要做的。
如果您不是执行回调的人,则需要使用TaskCompletionSource
来提供自己的Task
。在回调方法中,您将使用tcs.SetResult(result)
(或SetException
,如果需要)。最简单的方法是使用闭包:
var tcs = new TaskCompletionSource<MyResult>();
obj.Callback = result => { doWhateverYouNeed(); tcs.SetResult(result); };
return tcs.Task.GetAwaiter().GetResult();