web-dev-qa-db-fra.com

Le moyen le plus simple de faire un feu et oublier la méthode en C #?

J'ai vu dans WCF qu'ils ont l'attribut [OperationContract(IsOneWay = true)]. Mais WCF semble assez lent et lourd juste pour créer une fonction non bloquante. Idéalement, il devrait y avoir quelque chose comme du vide non statique statique 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 bloquante 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 liront ceci devraient se demander s'ils veulent réellement que la méthode se termine. (Voir réponse n ° 2 en haut) Si la méthode doit se terminer, à certains endroits, comme une application ASP.NET, vous devrez faire quelque chose pour bloquer et maintenir le thread actif. Sinon, cela pourrait conduire à "exécuter au feu, oublier, mais jamais réellement", auquel cas, il serait évidemment plus simple de ne pas écrire de code du tout. ( ne bonne description de comment cela fonctionne dans ASP.NET )

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

(cinq ans plus tard...)

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

comme indiqué par luisperezphd .

250
Will

Pour C # 4.0 et plus récent, il me semble que la meilleure réponse est maintenant donnée ici par Ade Miller: Le moyen le plus simple de faire un feu sans avoir recours à la méthode en c # 4.

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

Ou même...

Task.Factory.StartNew(FireAway);

Ou...

new Task(FireAway).Start();

FireAway est

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

Ainsi, en raison de la diversité des noms de méthodes et de méthodes, la version de pool de threads est dépassée de six à dix-neuf caractères en fonction de celui que vous avez choisi :)

ThreadPool.QueueUserWorkItem(o => FireAway());
37
Patrick Szalapski

Pour ajouter à réponse de Will , s'il s'agit d'une application console, ajoutez simplement un AutoResetEvent et un WaitHandle pour l'empêcher de se terminer avant la fin du thread de travail:

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();
    }
}
17
Kev

Pour .NET 4.5:

Task.Run(() => FireAway());
16
David Murdoch

Un moyen simple est de créer et de démarrer un fil de discussion avec 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 sur ThreadPool.QueueUserWorkItem, vous pouvez nommer votre nouveau thread pour faciliter le débogage. De plus, n'oubliez pas d'utiliser une gestion d'erreur étendue dans votre routine car toutes les exceptions non gérées en dehors d'un débogueur planteront brutalement votre application:

enter image description here

14
Robert Venables

La méthode recommandée pour ce faire lorsque vous utilisez Asp.Net et .Net 4.5.2 consiste à utiliser QueueBackgroundWorkItem. Voici une classe d'assistance:

public static class BackgroundTaskRunner
{     
    public static void FireAndForgetTask(Action action)
    {
        HostingEnvironment.QueueBackgroundWorkItem(cancellationToken => // .Net 4.5.2 required
        {
            try
            {
                action();
            }
            catch (Exception e)
            {
                // TODO: handle exception
            }
        });
    }

    /// <summary>
    /// Using async
    /// </summary>
    public static void FireAndForgetTask(Func<Task> action)
    {
        HostingEnvironment.QueueBackgroundWorkItem(async cancellationToken => // .Net 4.5.2 required
        {
            try
            {
                await action();
            }
            catch (Exception e)
            {
                // TODO: handle exception
            }
        });
    }
}

Exemple d'utilisation:

BackgroundTaskRunner.FireAndForgetTask(() =>
{
    FireAway();
});

ou en utilisant async:

BackgroundTaskRunner.FireAndForgetTask(async () =>
{
    await FireAway();
});

Cela fonctionne très bien sur les sites Web Azure.

Référence: tilisation de QueueBackgroundWorkItem pour planifier des tâches en arrière-plan à partir d'une application ASP.NET dans .NET 4.5.2

10
Augusto Barreto

Appeler beginInvoke et ne pas intercepter 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'appel (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 appelé génère une exception, celle-ci est mise en cache dans les données d'appel. Tant que vous n'avez pas appelé EndInvoke, il reste en mémoire. 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 en place jusqu'à la fermeture du processus car les données sont gérées en interne par le code d'invocation. Je suppose que le GC pourrait éventuellement le collecter, mais je ne sais pas comment le GC pourrait savoir que vous avez abandonné les données, mais que vous avez mis beaucoup de temps à les récupérer. J'en doute. Une fuite de mémoire peut donc se produire.

Plus d'informations peuvent être trouvées sur http://haacked.com/archive/2009/01/09/asynchronous-fire-and-forget-with-lambdas.aspx

7
Manoj Aggarwal

L’approche la plus simple .NET 2.0 et ultérieure utilise le modèle de programmation asynchrone (par exemple, BeginInvoke sur un délégué):

static void Main(string[] args)
{
      new MethodInvoker(FireAway).BeginInvoke(null, null);

      Console.WriteLine("Main: " + Thread.CurrentThread.ManagedThreadId);

      Thread.Sleep(5000);
}

private static void FireAway()
{
    Thread.Sleep(2000);

    Console.WriteLine("FireAway: " + Thread.CurrentThread.ManagedThreadId );  
}
3
Ash

Presque 10 ans plus tard:

Task.Run(FireAway);

J'ajouterais la gestion des exceptions et la journalisation à l'intérieur de FireAway

3
Oscar Fraxedas