162 votes

La façon la plus simple de faire une méthode "fire and forget" en C# ?

J'ai vu dans WCF ils ont le [OperationContract(IsOneWay = true)] l'attribut. Mais WCF semble un peu lent et lourd juste pour faire créer une fonction non bloquante. Idéalement il y aurait quelque chose comme static void nonblocking MethodFoo(){} mais je ne pense pas que cela existe.

Quel est le moyen le plus rapide de créer un appel de méthode non bloquant en C# ?

Par exemple

class Foo
{
    static void Main()
    {
        FireAway(); //No callback, just go away
        Console.WriteLine("Happens immediately");
    }

    static void FireAway()
    {
        System.Threading.Thread.Sleep(5000);
        Console.WriteLine("5 seconds later");
    }
}

NB : Tous ceux qui lisent ceci devraient réfléchir pour savoir s'ils veulent réellement que la méthode se termine. (Si la méthode doit se terminer, alors dans certains endroits, comme une application ASP.NET, vous devrez faire quelque chose pour bloquer et garder le fil en vie. Dans le cas contraire, cela pourrait conduire à une situation de "feu-forget-mais-ne-jamais-exécuter-au-fait", auquel cas, bien sûr, il serait plus simple de ne pas écrire de code du tout. ( Une bonne description de la façon dont cela fonctionne en ASP.NET )

292voto

Will Points 76760
ThreadPool.QueueUserWorkItem(o => FireAway());

(cinq ans plus tard...)

Task.Run(() => FireAway());

comme l'a souligné luisperezphd .

0 votes

Vous gagnez. Non, vraiment, cela semble être la version la plus courte, à moins que vous ne l'encapsuliez dans une autre fonction.

14 votes

J'y pense... dans la plupart des applications, c'est parfait, mais dans la vôtre, l'application console se terminera avant que FireAway n'envoie quoi que ce soit à la console. Les threads ThreadPool sont des threads d'arrière-plan et meurent lorsque l'application meurt. D'autre part, l'utilisation d'un Thread ne fonctionnerait pas non plus car la fenêtre de la console disparaîtrait avant que FireAway n'essaie d'écrire dans la fenêtre.

3 votes

Il n'y a aucun moyen d'avoir un appel de méthode non bloquant dont l'exécution est garantie, donc c'est en fait la réponse la plus précise à la question IMO. Si vous devez garantir l'exécution, alors un blocage potentiel doit être introduit via une structure de contrôle telle que AutoResetEvent (comme Kev l'a mentionné).

41voto

Patrick Szalapski Points 1756

Pour C# 4.0 et les versions plus récentes, il me semble que la meilleure réponse est maintenant donnée ici par Ade Miller : La façon la plus simple de faire une méthode "fire and forget" en c# 4.0

Task.Factory.StartNew(() => FireAway());

Ou même...

Task.Factory.StartNew(FireAway);

Ou...

new Task(FireAway).Start();

FireAway es

public static void FireAway()
{
    // Blah...
}

Ainsi, en vertu de l'uniformité des noms de classes et de méthodes, cette méthode bat la méthode du version threadpool de six à dix-neuf caractères, selon la version que vous celle que vous choisissez :)

ThreadPool.QueueUserWorkItem(o => FireAway());

11 votes

Ce qui m'intéresse le plus, c'est que les meilleures réponses soient facilement disponibles pour les bonnes questions. J'encourage vivement les autres à faire de même.

3 votes

Selon stackoverflow.com/help/référencement Dans le cas d'une citation, vous êtes tenu d'utiliser des guillemets pour indiquer que vous citez une autre source. En l'état, votre réponse semble être votre propre travail. Votre intention en réaffichant une copie exacte de la réponse aurait pu être atteinte avec un lien dans un commentaire.

1 votes

Comment passer des paramètres à FireAway ?

18voto

Kev Points 60744

À ajouter à Réponse de Will s'il s'agit d'une application console, il suffit d'ajouter une balise AutoResetEvent et un WaitHandle pour éviter qu'il ne se termine avant que le fil de travail ne soit terminé :

Using System;
Using System.Threading;

class Foo
{
    static AutoResetEvent autoEvent = new AutoResetEvent(false);

    static void Main()
    {
        ThreadPoolQueueUserWorkItem(new WaitCallback(FireAway), autoEvent);
        autoEvent.WaitOne(); // Will wait for thread to complete
    }

    static void FireAway(object stateInfo)
    {
        System.Threading.Thread.Sleep(5000);
        Console.WriteLine("5 seconds later");
        ((AutoResetEvent)stateInfo).Set();
    }
}

0 votes

Si ce n'est pas une application console et que ma classe C# est COM Visible, votre AutoEvent fonctionnera-t-il ? Est-ce que autoEvent.WaitOne() bloque ?

5 votes

@dan_l - Aucune idée, pourquoi ne pas poser une nouvelle question et faire référence à celle-ci ?

0 votes

@dan_l, oui WaitOne est toujours bloqué et AutoResetEvent fonctionnera toujours

15voto

Robert Venables Points 4599

Une méthode simple consiste à créer et à démarrer un fil de discussion avec un lambda sans paramètre :

(new Thread(() => { 
    FireAway(); 
    MessageBox.Show("FireAway Finished!"); 
}) { 
    Name = "Long Running Work Thread (FireAway Call)",
    Priority = ThreadPriority.BelowNormal 
}).Start();

En utilisant cette méthode par-dessus ThreadPool.QueueUserWorkItem, vous pouvez nommer votre nouveau thread pour faciliter le débogage. N'oubliez pas non plus d'utiliser un système de gestion des erreurs complet dans votre routine, car toute exception non gérée en dehors d'un débogueur entraînera une panne brutale de votre application :

enter image description here

0 votes

+1 pour quand vous avez besoin d'un thread dédié en raison d'un processus long ou bloquant.

7voto

Manoj Aggarwal Points 61

Appeler beginInvoke et ne pas attraper EndInvoke n'est pas une bonne approche. La réponse est simple : La raison pour laquelle vous devez appeler EndInvoke est que les résultats de l'invocation (même s'il n'y a pas de valeur de retour) doivent être mis en cache par .NET jusqu'à ce que EndInvoke soit appelé. Par exemple, si le code invoqué lève une exception, celle-ci est mise en cache dans les données d'invocation. Elle reste en mémoire jusqu'à ce que vous appeliez EndInvoke. Après avoir appelé EndInvoke, la mémoire peut être libérée. Dans ce cas particulier, il est possible que la mémoire reste jusqu'à ce que le processus s'arrête, car les données sont maintenues en interne par le code d'invocation. Je suppose que le GC pourrait éventuellement les collecter, mais je ne sais pas comment le GC saurait que vous avez abandonné les données ou que vous avez simplement pris beaucoup de temps pour les récupérer. Je doute qu'il le sache. Une fuite de mémoire peut donc se produire.

Vous trouverez plus d'informations sur http://haacked.com/archive/2009/01/09/asynchronous-fire-and-forget-with-lambdas.aspx

3 votes

Le GC peut dire quand les choses sont abandonnées si aucune référence n'existe à leur sujet. Si BeginInvoke renvoie un objet de type "wrapper" qui contient une référence à l'objet qui contient les données de résultat réelles, mais qui n'est pas référencé par celui-ci. L'objet "wrapper" devient éligible pour la finalisation une fois que toutes les références à celui-ci ont été abandonnées.

0 votes

Je remets en question le fait que cette approche ne soit "pas bonne" ; testez-la, avec les conditions d'erreur qui vous préoccupent, et voyez si vous avez des problèmes. Même si le ramassage des ordures n'est pas parfait, cela n'affectera probablement pas la plupart des applications de façon notable. Selon Microsoft, "Vous pouvez appeler EndInvoke pour récupérer la valeur de retour du délégué, si nécessaire, mais ce n'est pas obligatoire. EndInvoke se bloquera jusqu'à ce que la valeur de retour puisse être récupérée" : msdn.microsoft.com/fr/us/library/0b1bf3y3(v=vs.90).aspx

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