我是一个C#应用程序,我需要在其中调用四个异步任务(内部调用第三方Web服务)。 每个任务都会返回一个布尔值true / false,具体取决于成功或失败。
一旦这4个任务中的任何一个返回true,我需要调用另一个方法,例如PostProcessing()。 对于例如如果#2方法调用返回true,我需要中止处理并调用PostProcessing()方法。
如果所有任务都返回false,我不想调用PostProcessing()。
实现这种方法的最佳方法是什么?是Task.ContinueWith()??
感谢。
答案 0 :(得分:3)
我会使用Microsoft的Reactive Framework(Rx)来实现这一目标 - 它变得简单易行。
首先,我假设您正在使用的方法的签名是:
public async Task<bool> WebService1()
public async Task<bool> WebService2()
public async Task<bool> WebService3()
public async Task<bool> WebService4()
public void PostProcessing()
现在你可以设置这个使用Rx:
var webservices = new Func<Task<bool>>[]
{
WebService1, WebService2, WebService3, WebService4,
};
IObservable<bool> query =
webservices
.ToObservable()
.SelectMany(ws => Observable.FromAsync(ws))
.Where(b => b == true)
.Take(1);
IDisposable subscription = query.Subscribe(b => PostProcessing());
这很好地异步调用所有四个Web异步服务(Observable.FromAsync(ws)
).ToObservable().SelectMany(...)
,然后将结果过滤为仅返回true
(.Where(b => b == true)
)的结果。它最终只需要一个结果.Take(1)
。
然后,如果它确实得到一个结果 - 必须是true
- 那么当发生这种结果时它会调用PostProcessing
。
如果您需要在任何网络服务返回之前中止,您可以随时致电subscription.Dispose()
。
简单。
答案 1 :(得分:1)
就个人而言,我可能会使用Reactive Extensions(如@Enigmativity)。但是,如果您想避免使用Reactive Extensions,那么我认为您可以将Task.WhenAny
与while循环结合使用。如果我要走这条路线,我会创建一个静态方法来保持干净。所以像这样:
public static class TaskExtensions
{
public static async Task<Task<TResult>> WhenAnyWithPredicate<TResult>(Func<Task<TResult>, bool> predicate, params Task<TResult>[] tasks)
{
if (tasks == null)
{
throw new ArgumentNullException();
}
if (tasks.Length == 0)
{
return null;
}
// Make a safe copy (in case the original array is modified while we are awaiting).
tasks = tasks.ToArray();
// Await the first task.
var result = await Task.WhenAny(tasks);
// Test the task and await the next task if necessary.
while (tasks.Length > 0 && !predicate(result))
{
tasks = tasks.Where(x => x != result).ToArray();
if (tasks.Length == 0)
{
result = null;
}
else
{
result = await Task.WhenAny(tasks);
}
}
// Return the result.
return result;
}
}
你可以这样使用它。
CancellationTokenSource cts = new CancellationTokenSource();
// Start your four tasks.
var tasks = new Task<bool>[]
{
CreateTask1WithCancellationToken(cts.Token),
CreateTask2WithCancellationToken(cts.Token),
CreateTask3WithCancellationToken(cts.Token),
CreateTask4WithCancellationToken(cts.Token),
}
// Wait for the first task with a result of 'true' to complete.
var result = await TaskExtensions.WhenAnyWithPredicate(x => x.Status == TaskStatus.RanToCompletion && x.Result, tasks);
// Cancel the remaining tasks (if any).
cts.Cancel();
// If you have a nonnull task then success!
if (result != null)
{
PostProcessing();
}
答案 2 :(得分:0)
我会做这样的事情:
var cancellationSource = new CancellationTokenSource();
// Start the tasks
var tasks = new List<Task<bool>>
{
WebService(cancellationSource.Token),
WebService(cancellationSource.Token),
WebService(cancellationSource.Token),
WebService(cancellationSource.Token)
};
// Wait until first task returns true
bool success = false;
while (!success && tasks.Any())
{
var completedTask = await Task.WhenAny(tasks);
try
{
// It either completed or failed, try to get the result
success = await completedTask;
}
catch (Exception ex)
{
// ignore or log exception
}
tasks.Remove(completedTask);
}
// Cancel remaining tasks
cancellationSource.Cancel();
// Ensure all tasks have completed to avoid unobserved exceptions
if (tasks.Any())
{
try
{
await Task.WhenAll(tasks);
}
catch (Exception ex)
{
// ignore or log exception
}
}
if (success)
{
PostProcessing();
}
不像Enigmativity的解决方案,但直截了当;)
答案 3 :(得分:-1)
我在手机上,所以我很难写出任何代码。但根据我的理解,取消令牌就是你想要的。请看这里https://msdn.microsoft.com/en-us/library/system.threading.cancellationtoken(v=vs.110).aspx
然后,您可以在调用方法时注册取消请求的回调。请查看示例https://msdn.microsoft.com/en-us/library/ee191554(v=vs.110).aspx