web-dev-qa-db-fra.com

Qu'est-ce qui rend une méthode thread-safe? Quelles sont les règles?

Existe-t-il des règles/directives générales sur ce qui rend une méthode thread-safe? Je comprends qu'il y a probablement un million de situations uniques, mais qu'en est-il en général? Est-ce si simple?

  1. Si une méthode n'accède qu'aux variables locales, son thread est sécurisé.

Est-ce que c'est ça? Est-ce que cela s'applique aussi aux méthodes statiques?

Une réponse, fournie par @Cybis, était:

Les variables locales ne peuvent pas être partagées entre les threads car chaque thread obtient sa propre pile.

Est-ce le cas également pour les méthodes statiques?

Si un objet de référence est passé à une méthode, est-ce que cela brise la sécurité des threads? J'ai fait des recherches, et il y a beaucoup de choses à propos de certains cas, mais j'espérais pouvoir définir, en utilisant seulement quelques règles, des lignes directrices à suivre pour s'assurer qu'une méthode est thread-safe.

Donc, je suppose que ma dernière question est: "Existe-t-il une courte liste de règles définissant une méthode thread-safe? Si oui, quelles sont-elles?"

EDIT
Beaucoup de points positifs ont été présentés ici. Je pense que la vraie réponse à cette question est: "Il n'y a pas de règles simples pour assurer la sécurité des threads." Cool. Bien. Mais en général Je pense que la réponse acceptée fournit un bon résumé succinct. Il y a toujours des exceptions. Ainsi soit-il. Je peux vivre avec ça.

141
Bob Horn

Si une méthode (instance ou statique) fait uniquement référence à des variables définies dans cette méthode, elle est thread-safe, car chaque thread a sa propre pile:

Dans ce cas, plusieurs threads pourraient appeler ThreadSafeMethod simultanément sans problème.

public class Thing
{
    public int ThreadSafeMethod(string parameter1)
    {
        int number; // each thread will have its own variable for number.
        number = parameter1.Length;
        return number;
    }
}

Cela est également vrai si la méthode appelle une autre méthode de classe qui fait uniquement référence à des variables localement étendues:

public class Thing
{
    public int ThreadSafeMethod(string parameter1)
    {
        int number;
        number = this.GetLength(parameter1);
        return number;
    }

    private int GetLength(string value)
    {
        int length = value.Length;
        return length;
    }
}

Si une méthode accède à des propriétés ou des champs (instance ou statique) (état de l'objet), vous devez utiliser des verrous pour vous assurer que les valeurs ne sont pas modifiées par un autre thread.

public class Thing
{
    private string someValue; // all threads will read and write to this same field value

    public int NonThreadSafeMethod(string parameter1)
    {
        this.someValue = parameter1;

        int number;

        // Since access to someValue is not synchronised by the class, a separate thread
        // could have changed its value between this thread setting its value at the start 
        // of the method and this line reading its value.
        number = this.someValue.Length;
        return number;
    }
}

Vous devez savoir que tous les paramètres transmis à la méthode qui ne sont ni une structure ni une immuable peuvent être mutés par un autre thread en dehors du cadre de la méthode.

Pour garantir une concurrence adéquate, vous devez utiliser le verrouillage.

pour plus d'informations, voir référence de l'instruction de verrouillage C # et ReadWriterLockSlim .

lock est surtout utile pour fournir une fonctionnalité à la fois,
ReadWriterLockSlim est utile si vous avez besoin de plusieurs lecteurs et d'un seul auteur.

129
Trevor Pilley

Si une méthode n'accède qu'aux variables locales, son thread est sécurisé. Est-ce que c'est ça?

Absolument pas. Vous pouvez écrire un programme avec une seule variable locale accessible à partir d'un seul thread mais néanmoins non threadsafe:

https://stackoverflow.com/a/8883117/88656

Est-ce que cela s'applique aussi aux méthodes statiques?

Absolument pas.

Une réponse, fournie par @Cybis, était: "Les variables locales ne peuvent pas être partagées entre les threads car chaque thread obtient sa propre pile."

Absolument pas. La caractéristique distinctive d’une variable locale est qu’elle est uniquement visible à partir de la portée locale , et non qu’elle est allouée sur le pool temporaire . Il est parfaitement légal et possible d'accéder à la même variable locale à partir de deux threads différents. Vous pouvez le faire en utilisant des méthodes anonymes, lambdas, blocs itérateur ou asynchrone. méthodes.

Est-ce le cas également pour les méthodes statiques?

Absolument pas.

Si un objet de référence est passé à une méthode, est-ce que cela brise la sécurité des threads?

Peut être.

J'ai fait des recherches, et il y a beaucoup de choses à propos de certains cas, mais j'espérais pouvoir définir, en utilisant seulement quelques règles, des lignes directrices à suivre pour s'assurer qu'une méthode est thread-safe.

Vous allez devoir apprendre à vivre avec déception. C'est un sujet très difficile.

Donc, je suppose que ma question ultime est: "Existe-t-il une courte liste de règles définissant une méthode thread-safe?

Nan. Comme vous l'avez vu dans mon exemple précédent , une méthode vide peut être non thread-safe . Vous pourriez aussi bien vous demander "existe-t-il une courte liste de règles garantissant qu'une méthode est correcte ". Non, il n'y en a pas. La sécurité des fils n’est rien de plus qu’un type de correction extrêmement compliqué.

De plus, le fait que vous posiez la question indique votre incompréhension fondamentale à propos de la sécurité des fils. La sécurité des threads est une propriété globale , pas une propriété locale d'un programme. La raison pour laquelle il est si difficile de comprendre est parce que vous devez avoir une connaissance complète du comportement de threading du programme entier afin de garantir sa sécurité.

Encore une fois, regardez mon exemple: chaque méthode est triviale . C'est la façon dont les méthodes interagissent les unes avec les autres à un niveau "global" qui rend le programme dans l'impasse. Vous ne pouvez pas regarder toutes les méthodes et les cocher comme étant "sûres" et vous attendre ensuite à ce que le programme tout entier soit sûr, pas plus que vous ne pouvez en conclure que, parce que votre maison est faite de briques à 100% non creuses, elle l'est aussi non-creux. Le creux d'une maison est une propriété globale de l'ensemble, pas un ensemble des propriétés de ses parties.

102
Eric Lippert

Il n'y a pas de règle absolue.

Voici quelques règles pour sécuriser les threads de code dans .NET et pourquoi ce ne sont pas de bonnes règles:

  1. La fonction et toutes les fonctions qu'il appelle doivent être pures (pas d'effets secondaires) et utiliser des variables locales. Bien que cela rende votre code thread-safe, il existe également très peu de choses intéressantes que vous pouvez faire avec cette restriction dans .NET.
  2. Toute fonction qui opère sur un objet commun doit lock sur une chose commune. Tous les verrous doivent être effectués dans le même ordre. Cela sécurisera le thread de code, mais il sera incroyablement lent et vous pourriez aussi ne pas utiliser plusieurs threads.
  3. ...

Aucune règle ne rend le thread de code sûr, la seule chose à faire est de vous assurer que votre code fonctionnera peu importe le nombre de fois qu'il est exécuté de manière active, chaque thread peut être interrompu à tout moment, chaque thread étant dans. son propre état/emplacement, et ceci pour chaque fonction (statique ou autre) qui accède aux objets communs.

9
earlNameless

Il doit être synchronisé, à l'aide d'un verrou d'objet, sans état ou immuable.

lien: http://docs.Oracle.com/javase/tutorial/essential/concurrency/immutable.html

4
kasavbere