Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

  1. #1
    Membre du Club
    Exception : Multiples tâches asynchrones
    Bonjour, bonsoir à tous

    Pour situer le contexte, je dispose d'un projet contenant une référence de services (WCF) dans le but final de faire un petit outil de tests / bench
    sur des services ciblés.

    Concernant la structure, j'ai décidé de faire une méthode générique qui aura pour but de récupérer un service via son nom de méthode,
    ses paramètres puis d'invoker la méthode, le tout de manière asynchrone.

    Méthode générique :

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
     
            public async Task<T> ExecuteMethod<T>(string nom, object[] param)
            {
                using var context = new SvDataServiceClient(new InstanceContext(Instance));
     
                Type type = context.GetType();
                MethodInfo methode = type.GetMethod(nom);
     
                if (methode != null)
                {
                    var parametres = methode.GetParameters();
                    if (parametres.Length == 0)
                        param = null;
                }
     
                var result = (Task<T>)methode?.Invoke(context, param);
     
                if (result != null && !result.IsFaulted) 
                    return await result;
     
                return default;
            }


    En utilisation simple, la méthode fonctionne bien. J'ai crée une autre classe que j'ai appelé ManagerCore dans laquelle j'appelle ma
    méthode générique et fais quelques tests. (Je retourne tous les résultats sous forme de JSON).

    Méthode Task d'appel de service :
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
     
            public async Task<string[]> GetUserFiltersAsync()
            {
                var invokeMethod = new MethodInvoker(this);
                var benchMark = new BenchMark {
                    Action = "GetUserFiltersAsync",
                    Debut = DateTime.Now.Date,
                    Parametres = new object[] { 2, "SvDataObject.Poco.Article" },
                    ParametresRequis = invokeMethod.ExtractParams("GetUserFiltersAsync")
                };
     
                var BenchTime = new Stopwatch();
                BenchTime.Start();
     
                var userFilters = await invokeMethod.ExecuteMethod<UserFilter[]>(benchMark.Action, benchMark.Parametres);
     
                BenchTime.Stop();
     
                var userFiltersJson = await Task.Factory.StartNew(() => JsonConvert.SerializeObject(userFilters, Formatting.Indented));
     
                benchMark.Resultat = new object[] { userFilters.Select(x => x.FilterName) };
                benchMark.Duree = BenchTime.ElapsedMilliseconds;
                benchMark.Fin = DateTime.Now.Date;
     
                var benchJson = await Task.Factory.StartNew(() => JsonConvert.SerializeObject(benchMark, Formatting.Indented));
     
                return new[] { userFiltersJson, benchJson };
            }


    Seulement, je suis actuellement sur une feature qui servira à créer des scénarios, en gros,
    faire une succession de tâches asynchrones (appels de services). Pour ça j'ai utilisé
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    Task.WhenAll(tasks);


    Méthode de scénario :
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
     
            public async Task<Dictionary<int, string>> Scenario1()
            {
                var managerCore = new ManagerCore();
                var jsonParser = new JsonParser();
     
                var tasks = new List<Task<string[]>>
                {
                    managerCore.GetUserFiltersAsync(),
                    managerCore.GetCommandeFournisseursAsync(),
                    managerCore.GetCommandeFournisseurAsync(),
                    managerCore.GetLignesCommandeFournisseursAsync(),
                    managerCore.GetOperateursAsync(),
                };
     
                await Task.WhenAll(tasks);
     
                foreach (var task in tasks) task.Dispose();
     
                return jsonParser.ParseMultipleJson(new List<string>
                {
                    tasks[0].Result[1], 
                    tasks[1].Result[1], 
                    tasks[2].Result[1], 
                    tasks[3].Result[1], 
                    tasks[4].Result[1], 
                });
            }


    Puis enfin, j'appelle la méthode de mon scénario de cette manière :

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
                var scenario = await new ExecuteScenario().Scenario1();
     
                foreach (var item in scenario)
                {
                   // Récup des données du dico retourné.
                }


    Je rencontre différents problèmes :

    Le premier :

    Lorsque je démarre mon appli (winform) et que je fais un premier appel, n'importe le quel,
    il met un temps interminable à s'exécuter, pourtant la méthode passe bien (log de console) et c'est uniquement sur le premier appel.
    Et de manière aléatoire, les appels bloquent indéfiniment, j'ai beau attendre, rien ne se passe (voir ci-dessous) :



    Le second (qui survient de manière aléatoire après plusieurs appels async d'un scénario ) :

    Mon application plante, principalement au niveau de la méthode générique et me retourne


    Log console (Host WCF) :


    Le troisième :

    Mon application plante aussi au niveau de la méthode générique mais cette fois-ci, l'erreur n'est pas la même
    et à vrai dire j'ai aucune idée d'où elle peut venir :



    Dernière erreur :

    Lorsque je lance en premier un scénario (au lancement du HOST WCF), j'ai l'erreur suivante

    System.ServiceModel.FaultException`1*: 'Impossible d'utiliser le contexte lors de la création du modèle.
    L'exception peut être levée si le contexte est utilisé dans la méthode OnModelCreating ou si la même instance de contexte
    est accessible par plusieurs threads simultanément. Notez qu'il n'est pas garanti que les membres
    d'instance de DbContext et les classes associées soient thread-safe.'
    A savoir que mon ws est configuré en
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    ConcurrencyMode = ConcurrencyMode.Multiple, InstanceContextMode = InstanceContextMode.Single]


    J'ai l'impression que le/les problème(s) se situe(nt) au niveau du using context et de l'InstanceContext ?? A voir..

    Merci d'avoir pris le temps de lire, en espérant avoir quelques pistes.

    Merci,
    Cordialement.

  2. #2
    Expert éminent sénior
    Plusieurs choses :
    1. les Tasks sont disposées avant d'accéder à leur résultat. Cela peut avoir un résultat inattendu (et peut potentiellement expliquer certaines exceptions "aléatoires")
    2. mettre les Task dans une liste ne signifie pas que les taches sont exécutées de manière séquentielle ! Elles seront exécutées en parallèle
    3. le winform est un contexte particulier, dans le sens où toutes les Task sont exécutés sur le thread UI. Si ce n'est pas un prérequis (ce qui semble le cas), l'usage de .ConfigureAwait(false) peut accélérer les choses
    4. pour exécuter une tâche après une autre, il faut utiliser .ContinueWith


    Le premier point doit absolument être corrigé, puis refaire les tests. Certaines erreurs pourront être corrigées.

    Le point 3 peut expliquer les temps assez long.

    Et surtout, ne pas mélanger les async/await et les méthodes classique style Task.Wait,car cela peut générer des blocages dans un contexte winform.
    François DORIN
    Consultant informatique : conception, modélisation, développement (C#/.Net et SQL Server)
    Site internet | Profils Viadéo & LinkedIn
    ---------
    Page de cours : fdorin.developpez.com
    ---------
    N'oubliez pas de consulter la FAQ C# ainsi que les cours et tutoriels

  3. #3
    Membre du Club
    Bonjour,

    Merci pour votre réponse, je reviens (un peu tard il est vrai) vers cette discussion pour la mettre à jour.
    J'ai donc pu résoudre la majorité de mes problèmes, en effet ma gestion des Tasks était mal faite, j'ai donc tout revu et migré vers WPF par la même occasion.
    Cela dit, après pas mal de doc à propos des tâches asynchrones, le .ContinueWith revient à de multiples reprises comme étant de "bas niveau" et qu'il était
    préférable d'utiliser à la place await..
    Si jamais vous avez quelques infos là dessus !

    Merci,
    Cordialement.

  4. #4
    Membre habitué
    Désolé, j'arrive après "la bataille" ^^, ravi que vous ayez su reprendre votre gestion sur les tasks.

    Toutefois, je ne comprends pas votre remarque entre continuewith et await, car ce sont deux notions différentes qui sont manipulées. De ce fait, je suis intéressé par la documentation que vous avez pu lire (si vous l'avez encore sous la main) car soit la documentation lue ne donne pas une information suffisamment explicite du sujet (qui sur le sujet des task est assez fréquent :/), soit raconte des inepties ce qui est aussi souvent le cas... ou alors c'est mon cerveau qui me joue un vilain tour . Dans tous les cas, cela m'aidera à mieux vous aider dans la compréhension entre ces deux notions, et que vous puissiez aussi communiquer dessus à l'avenir .
    Mon blog est sur https://arphonis.fr et bientôt d'autres fonctionnalités seront disponible dessus.

  5. #5
    Expert éminent sénior
    Citation Envoyé par mathisdu42 Voir le message
    Cela dit, après pas mal de doc à propos des tâches asynchrones, le .ContinueWith revient à de multiples reprises comme étant de "bas niveau" et qu'il était
    préférable d'utiliser à la place await..
    Si jamais vous avez quelques infos là dessus !
    Le .ContinueWith est tout le temps utilisable, et à partir de la version 4 du framework. Pour async/await, il faut attendre la version 4.5 tout en évitant de l'utiliser avec les méthodes "classiques" comme Task.Wait.

    .ContinueWith permet également une gestion plus fine, en précisant, par exemple, dans quel cas continuer (tout le temps, uniquement en cas d'erreur, uniquement en cas de succès, etc.). De ce point de vue, oui, cette méthode peut être qualifiée de plus bas niveau. Mais c'est vraiment chercher la petite bête !
    François DORIN
    Consultant informatique : conception, modélisation, développement (C#/.Net et SQL Server)
    Site internet | Profils Viadéo & LinkedIn
    ---------
    Page de cours : fdorin.developpez.com
    ---------
    N'oubliez pas de consulter la FAQ C# ainsi que les cours et tutoriels

  6. #6
    Membre chevronné
    Citation Envoyé par mathisdu42 Voir le message
    Bonjour,

    Merci pour votre réponse, je reviens (un peu tard il est vrai) vers cette discussion pour la mettre à jour.
    J'ai donc pu résoudre la majorité de mes problèmes, en effet ma gestion des Tasks était mal faite, j'ai donc tout revu et migré vers WPF par la même occasion.
    Cela dit, après pas mal de doc à propos des tâches asynchrones, le .ContinueWith revient à de multiples reprises comme étant de "bas niveau" et qu'il était
    préférable d'utiliser à la place await..
    Si jamais vous avez quelques infos là dessus !

    Merci,
    Cordialement.
    Dans les articles de François Dorin tu peux suivre ses tutoriels sur le Pool de Thread.

###raw>template_hook.ano_emploi###