2 votes

Gestion des tâches avec différents résultats

J'ai un projet .NET Core 2.1 qui a une BackgroundService et je veux que sa responsabilité se limite à gérer l'enregistrement du résultat d'un groupe de tâches différentes qui peuvent renvoyer des valeurs différentes. Je veux regrouper tous leurs résultats dans une classe de gestionnaire de tâches pour enregistrer leurs résultats. Est-il possible d'avoir une List<Task> qui contiendra tous les Task à partir de ces méthodes asynchrones ?

Je ne veux pas avoir plusieurs Task pour chaque méthode que je veux await sur. Je préférerais qu'ils soient mis dans une List d'une manière ou d'une autre, car il pourrait y avoir la possibilité d'avoir plus que ces trois méthodes asynchrones que je veux que ce gestionnaire gère.

Je pensais faire quelque chose comme :

public class MyTaskManager : BackgroundService
{
    private readonly ILogger<MyTaskManager> _logger;
    private APIInvoker _invoker;

    public MyTaskManager (ILogger<MyTaskManager> logger, APIInvoker invoker)
    {
        _logger = logger;
        _invoker= invoker;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        List<Task<object>> tasks = new List<Task<object>>();

        tasks.Add(_invoker.GetImportWarningsAsync("1"));
        tasks.Add(_invoker.GetImportErrorsAsync("2"));
        tasks.Add(_invoker.GetImportStatusAsync("3"));
    }

GetImportWarningsAsync , GetImportErrorsAsync , GetImportStatusAsync sont définis comme suit :

internal async Task<string> GetImportWarningsAsync(...)
internal async Task<string> GetImportErrorsAsync(...)
internal async Task<ImportResponse> GetImportLeadStatusAsync(...)

Je suis confus sur le fait que je puisse faire tasks.Add(...) s'ils renvoient des types différents et que je les ajoute à une List<Task<object>> . Je ne pense pas que cela soit possible. Comment puis-je réaliser quelque chose comme ça ?

En fin de compte, je veux exécuter une méthode pour chaque Task en tasks quand l'un d'entre eux s'exécute.

eg.

protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
    List<Task<object>> tasks = new List<Task<object>>();

    tasks.Add(_invoker.GetImportWarningsAsync("1"));
    tasks.Add(_invoker.GetImportErrorsAsync("2"));
    tasks.Add(_invoker.GetImportStatusAsync("3"));

    Task<object> finishedTask = await Task.WhenAny(tasks);
    tasks.Remove(finishedTask);

    HandleTask(finishedTask, await finishedTask);
}

private void HandleTask(Task task, object value)
{
    if (value is ImportResponse)
    {
        _logger.LogInformation((value as ImportResponse).someProp); // Log something
    }
    else
    {
        // Any other object type will be logged here - In this case string.
        _logger.LogInformation(value.ToString());
    }
}

3voto

Blindy Points 26706

Les tâches ne sont pas covariantes de cette manière, mais rien ne vous empêche de couler le résultat selon vos besoins :

    var tasks = new List<Task<object>>();

    tasks.Add(((Func<Task<object>>)(async () => (object)await _invoker.GetImportWarningsAsync("1")))());
    tasks.Add(((Func<Task<object>>)(async () => (object)await _invoker.GetImportErrorsAsync("2")))());
    tasks.Add(((Func<Task<object>>)(async () => (object)await _invoker.GetImportStatusAsync("3")))());

1voto

Peter Csala Points 4906

Cette approche n'est probablement pas la plus agréable, mais elle fonctionne comme prévu.

public static class TaskExtensions
{
    public static async Task<(T1, T2)> WhenAll<T1, T2>(Task<T1> t1, Task<T2> t2)
    {
        return (await t1, await t2);
    }

    public static async Task<(T1, T2, T3)> WhenAll<T1, T2, T3>(Task<T1> t1, Task<T2> t2, Task<T3> t3)
    {
        return (await t1, await t2, await t3);
    }

    public static async Task<(T1, T2, T3, T4)> WhenAll<T1, T2, T3, T4>(Task<T1> t1, Task<T2> t2, Task<T3> t3, Task<T4> t4)
    {
        return (await t1, await t2, await t3, await t4);
    }

    //etc.
}

Nous profitons ici de ValueTuple .

Exemple d'utilisation :

var (warnings, errors, status) = await TaskExtensions.WhenAll(
   _invoker.GetImportWarningsAsync("1"),
   _invoker.GetImportErrorsAsync("2"),
   _invoker.GetImportStatusAsync("3") 
);

Ici, nous profitons de l'avantage de C# 7. déconstruction capacités.

Les types des variables déconstruites :

  • warnings : string
  • errors : string
  • status : ImportResponse

1voto

Theodor Zoulias Points 1088

Si vous le faites souvent, vous pouvez utiliser la méthode d'extension. ToObjectAsync montré ci-dessous :

public static async Task<object> ToObjectAsync<T>(this Task<T> task)
{
    return await task;
}

Exemple d'utilisation :

var tasks = new List<Task<object>>();

tasks.Add(_invoker.GetImportWarningsAsync("1").ToObjectAsync());
tasks.Add(_invoker.GetImportErrorsAsync("2").ToObjectAsync());
tasks.Add(_invoker.GetImportStatusAsync("3").ToObjectAsync());

Elle est essentiellement équivalente à celle de Blindy réponse juste un peu plus pratique.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X