IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
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

C# Discussion :

Interrompre requête trop longue


Sujet :

C#

  1. #1
    Membre confirmé
    Inscrit en
    Septembre 2009
    Messages
    126
    Détails du profil
    Informations forums :
    Inscription : Septembre 2009
    Messages : 126
    Par défaut Interrompre requête trop longue
    Bonjour,

    Je souhaiterai interrompre l’exécution d'une requête si pas de retour après un certain temps. Sur le principe ça à l'air simple mais je n'y arrive pas et pourtant j'ai cherché !
    Je précise que la requête à interrompre est le lancement d'une procédure stockée sur DB2, lancé en C# par un OledbCommand (ici nommé "cmd") :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    cmd.CommandText = "{CALL MABIB.MONPGM};"
    cmd.ExecuteNonReader();
    Il existe un paramètre "Timeout" dans OleDbcommand mais il ne fonctionne pas sur DB2 visiblement.
    J'ai donc essayé de chercher du coté de l'asynchrone, et j'ai testé à peu près tout ce que j'ai trouvé sur la toile concernant les threads, tasks, ExecuteNonReaderAsync(), await, AsyncCalback etc etc.

    systématiquement mon programme est bloqué durant toute l’exécution de la requête.
    J'ai donc du passer à coté de quelque choses au niveau de la gestion de l'asynchrone...

    Est-ce que quelqu'un a une idée de comment stopper l’exécution de la requête au bout de x secondes par ex ?
    Mon idée aussi naïve soit-elle serait de lancer dans un thread secondaires ma requête, puis dans le thread principal surveiller l'execution du thread secondaire, en vérifiant par ex s'il est terminé toutes les 100ms, et si au bout de 5 sec il n'est pas fini je le kill et je passe à la suite..

    Par avance merci pour votre aide !

  2. #2
    Expert éminent Avatar de Pol63
    Homme Profil pro
    .NET / SQL SERVER
    Inscrit en
    Avril 2007
    Messages
    14 204
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Puy de Dôme (Auvergne)

    Informations professionnelles :
    Activité : .NET / SQL SERVER

    Informations forums :
    Inscription : Avril 2007
    Messages : 14 204
    Par défaut
    ca a l'air d'être pris en charge pourtant
    https://www.ibm.com/support/knowledg...tProperty.html

    après oui dans les faits quand on fait quelque chose qui peut potentiellement dépasser quelques centaines de ms on passe sur un autre thread (ou une task qui fait ca)
    et kill le thread killera la connection et arretera la requete (enfin je connais pas db2 mais sur sql server c'est le cas)
    Cours complets, tutos et autres FAQ ici : C# - VB.NET

  3. #3
    Membre confirmé
    Inscrit en
    Septembre 2009
    Messages
    126
    Détails du profil
    Informations forums :
    Inscription : Septembre 2009
    Messages : 126
    Par défaut
    en fait cette option est valable pour un objet DB2 pour .net
    moi j'utilise le oledb.

    aurais tu un exemple de code qui permet d'appeler ce thread, puis de le kill quelques secondes après ? car dans tout ce que j'ai essayé, mon programme ne reprend pas la main... sauf si bien sur je met juste un Thread.Sleep(2000) par ex.
    mais à partir du moment ou je lance le cmd.ExecuteNonQuery(), ou même le ExecuteNonQueryAsync(), le thread prinicipal reste dessus jusqu'à ce qu'il rende la main...

  4. #4
    Membre Expert
    Homme Profil pro
    edi
    Inscrit en
    Juin 2007
    Messages
    941
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Gironde (Aquitaine)

    Informations professionnelles :
    Activité : edi

    Informations forums :
    Inscription : Juin 2007
    Messages : 941
    Par défaut
    Une technique que j'ai déjà vu faire pour créer un timeout sur une tâche asynchrone qui ne le prévoit pas est d'exécuter en parallèle un Task.Delay puis d'attendre la première tâche qui prend fin (Task.WhenAny). Par exemple :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    public async Task<int> ExecuteOrTimeoutCommandAsync(SqlCommand cmd, CancelletionTokenSource cts)
    {
    	Task<int> sqlTask = cmd.ExecuteNonQueryAsync(cts.Token);
    	Task timeout = Task.Delay(30000, cts.Token);
    	Task result = await Task.WhenAny(new [] { sqlTask, timeout });
    	cts.Cancel;
    	if(result == timeout) throw new TimeoutException());	
    	return sqlTask.Result;
    }

  5. #5
    Membre confirmé
    Inscrit en
    Septembre 2009
    Messages
    126
    Détails du profil
    Informations forums :
    Inscription : Septembre 2009
    Messages : 126
    Par défaut
    Merci Noxen très intéressant !

    Comment j'execute cette méthode dans ma tache principale ? j'ai essayé de faire un res = await ExecuteOrTimeoutCommandAsync(cmd, cts), mais j'ai une erreur de compilation : "L'opérateur await peut seulement être utilisé dans une tache async. Marquez cette méthode avec l'opérateur async et modifié son type de retour en Task"... pourtant elle est bien marquée en async et le retour est Task<int>.. j'ai essayé en modifiant le retour en Task uniquement j'ai la même erreur de compilation.

  6. #6
    Membre Expert
    Homme Profil pro
    edi
    Inscrit en
    Juin 2007
    Messages
    941
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Gironde (Aquitaine)

    Informations professionnelles :
    Activité : edi

    Informations forums :
    Inscription : Juin 2007
    Messages : 941
    Par défaut
    Je pense que le compilateur parle de ta méthode à toi ; si tu utilises await il faut que ta méthode soit marquée async. Si ce n'est pas possible tu peux aussi utiliser ContinueWith() sur la Task renvoyée par ma méthode et vérifier dedans si la Task en question est en erreur (Faulted), annulée (Cancelled) ou terminée (RanToCompletion) avant d'éventuellement récupérer la valeur de retour.

  7. #7
    Membre confirmé
    Inscrit en
    Septembre 2009
    Messages
    126
    Détails du profil
    Informations forums :
    Inscription : Septembre 2009
    Messages : 126
    Par défaut
    j'ai testé la méthode mais visiblement j'ai toujours le même soucis, mon pgm attend la fin de la requête pour rendre la main.. pourtant sur le papier ça semblait pas mal du tout !
    J'ai quand même une question car je crois que j'ai du mal à comprendre les subtilités de la limite entre synchrone et asynchrone...
    comment depuis ma fonctionne Main synchrone, j'appelle une fonction asynchrone ? et surtout comment j'attends son retour... car on ne peut utiliser await que dans une fonction async...
    j'ai un peu l'impression de tourner en boucle lol.. voici mon code actuel

    depuis ma fonction Main :

    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
     
    public void ExecMyPgm() // fonction synchrone
    {
    ...
    var cnx = new OleDbConnection(MYDSN);
    var cmd = new OleDbCommand()
    {
    connection = cnx,
    commandText = "{CALL MYBIB.MYPGM}"
    }
     
    var cts = new CancellationTokenSource();
    //ici je veux lancer la fonction qui "asynchrone"  mais en faite elle sera synchrone à ce moment puisque j’attends son retour, et gérera par contre des traitements asynchrones
    ExecAsyncCMD(cmd, cts).wait(); // mais ici ça ne fonctionne pas, normalement je devrais attendre 3 sec au max mais ça dure tout le temps de la sqlTask cf ci-dessous
    ...
    }
     
    //Fonction asynchrone
    private async Task<int> ExecAsyncCmd(OleDbCommand cmd, CancellationTokenSource cts)
    {
          Task<int> sqlTask = cmd.ExecuteNonQueryAsync(cts.Token);
          Task timeout = Task.Delay(3000, cts.Token);
          Task result = await Task.WhenAny(new [] { timeout2, timeout });
          cts.Cancel();
          if(result == timeout) throw new ShortException("Temps dépassé");
          return sqlT=ask.Result;
    }
    est-ce que tu pense que le ContinueWith() pourrais corriger ce pb ? ca devrait marcher avec wait() en théorie ?

  8. #8
    Membre Expert
    Homme Profil pro
    edi
    Inscrit en
    Juin 2007
    Messages
    941
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Gironde (Aquitaine)

    Informations professionnelles :
    Activité : edi

    Informations forums :
    Inscription : Juin 2007
    Messages : 941
    Par défaut
    Le paramètre CancellationTokenSource est optionnel, c'était juste pour permettre à l'utilisateur d'annuler un traitement (dans le cas présent ça permet aussi d'invoquer la fin de la tâche non terminée).

    Task.Wait te permet de bloquer le thread appelant pour attendre la complétion d'une tâche, il devrait très bien fonctionner ici. D'ailleurs tu aurais pu directement l'utiliser SqlCommand.ExecuteNonQueryAsync (avec une valeur de timeout : https://msdn.microsoft.com/fr-fr/lib...=vs.110).aspx) en lieu et place de la méthode que je proposais.

    Je suppose que dans ton code timeout2 provient d'un test.

  9. #9
    Membre confirmé
    Inscrit en
    Septembre 2009
    Messages
    126
    Détails du profil
    Informations forums :
    Inscription : Septembre 2009
    Messages : 126
    Par défaut
    c'est bien le problème, c'est que ça ne fonctionne pas, en tout cas pas avec DB2 et oledb. C'est pour ça que je cherche un moyen de contournement avec les taches asynchrones.
    Oui timeout2 vient d'un test j'ai oublier de l'enlever pardon, il faut remplacer par
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Task result = await Task.WhenAny(new [] { sqlTask, timeout })
    Quoi qu'il en soit ExecAsyncCMD ne me rend la main qu'après que sqlTask soit terminée, on dirait que la commande sql prend la main sur tout y compris le WhenAny qui du coup ne fonctionne pas. j'ai du mal à comprendre comment c'est possible.

  10. #10
    Membre Expert
    Homme Profil pro
    edi
    Inscrit en
    Juin 2007
    Messages
    941
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Gironde (Aquitaine)

    Informations professionnelles :
    Activité : edi

    Informations forums :
    Inscription : Juin 2007
    Messages : 941
    Par défaut
    Si tu fais :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    cmd.ExecuteNonQueryAsync.Wait(3000);
    dans la méthode ExecMyPgm() (au lieu de la méthode que je proposais), est-ce-que tu as toujours le problème ?

  11. #11
    Membre confirmé
    Inscrit en
    Septembre 2009
    Messages
    126
    Détails du profil
    Informations forums :
    Inscription : Septembre 2009
    Messages : 126
    Par défaut
    oui c'est pareil je doit attendre la fin de la requête pour récupérer la main, alors que je devrais attendre au max 3sec.
    C'est vraiment étrange comme comportement...

    j'avais même essayé une fonction un peu comme la tienne pour gérer les taches asynchrones avec un timeout:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
            private void RunAsync(Action action, int timeout)
            {
                EventWaitHandle waitHandle = new EventWaitHandle(false, EventResetMode.ManualReset);
                AsyncCallback callback = ar => waitHandle.Set();
                action.BeginInvoke(callback, null);
     
                if (!waitHandle.WaitOne(timeout))
                    throw new ShortException("Failed to complete in the timeout specified.");
            }
    dans l'action j'envoie une fonction qui fait juste le cmd.executeNonReader() mais résultat identique.
    Pourtant ca fonctionne bien si dans l'action je fait juste Thread.Sleep(10000).., je récupère bien la main au bout des 3 sec de timeout, mais avec le executeNonQuery() rien à faire.

Discussions similaires

  1. Requête trop longue en VBA
    Par NicoMon dans le forum VBA Access
    Réponses: 4
    Dernier message: 07/08/2007, 10h25
  2. requéte trop longue sous ie
    Par devboy dans le forum Général Conception Web
    Réponses: 2
    Dernier message: 16/05/2007, 16h43
  3. Réponses: 4
    Dernier message: 15/05/2007, 10h10
  4. [Requête] Requête trop longue
    Par Ithilien dans le forum Requêtes et SQL.
    Réponses: 2
    Dernier message: 08/01/2007, 10h58
  5. [MySQL] Requête trop longue ?
    Par Thomas1434 dans le forum PHP & Base de données
    Réponses: 14
    Dernier message: 24/03/2006, 21h55

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo