调用多个任务

时间:2016-12-01 23:18:42

标签: c#

我是一个C#应用程序,我需要在其中调用四个异步任务(内部调用第三方Web服务)。 每个任务都会返回一个布尔值true / false,具体取决于成功或失败。

一旦这4个任务中的任何一个返回true,我需要调用另一个方法,例如PostProcessing()。 对于例如如果#2方法调用返回true,我需要中止处理并调用PostProcessing()方法。

如果所有任务都返回false,我不想调用PostProcessing()。

实现这种方法的最佳方法是什么?是Task.ContinueWith()??

感谢。

4 个答案:

答案 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