Les variables locales dans le thread des méthodes statiques sont-elles sûres?

si j'ai une classe statique avec une méthode statique, les variables locales dans la méthode sont-elles sûres si plusieurs threads l'appellent?

static class MyClass {

  static int DoStuff(int n) {

    int x = n; // <--- Can this be modified by another thread?

    return x++;     
  }
}
25
demandé sur Vince Panuccio 2011-08-25 13:23:45
la source

1 ответов

les réponses à cette question qui indiquent que les sections locales sont stockées sur la pile et sont donc threadsafe sont incomplètes et peut-être dangereusement fausses.

les threads créent-ils leur propre portée lors de l'exécution de méthodes statiques?

Votre question contient une erreur commune. Une "portée" en C# est purement un concept de compilation; une "portée" est une région de texte du programmeentité (comme un variable ou type) peut être désigné par son nom sans réserve. Les portées aident à déterminer comment le compilateur établit une correspondance entre un nom et le concept qu'il représente.

les Threads ne créent pas de "scopes" à l'exécution car "scope" est purement un concept de compilation.

La portée d'un variable est connecté -- vaguement -- à son durée de vie; grossièrement parlant, la durée de vie d'une variable locale commence typiquement quand un thread de contrôle entre code correspondant au début de sa portée, et se termine lorsque le fil de la commande quitte. Cependant, le compilateur et le runtime disposent tous deux d'une discrétion considérable pour prolonger ou raccourcir cette durée de vie s'ils estiment que cela est efficace ou nécessaire.

en particulier, les locaux dans les blocs itérateurs et les locaux fermés de fonctions anonymes ont leur durée de vie prolongée au-delà du point où le contrôle quitte la portée.

cependant, rien de tout cela n'a à voir avec du fil de sécurité. Alors laissons tomber cette question mal formulée et passons à une formulation un peu meilleure:

Si j'ai une classe statique avec une méthode statique, sont les variables d'instance dans la méthode sûre si plusieurs threads de l'appeler?

Votre question contient une erreur. variables D'Instancechamps non statiques. Il est évident qu'il n'y a pas de champs non statiques dans une classe statique. Vous confondez exemple variables avec variables locales. La question que vous souhaitez poser est la suivante:

si j'ai une classe statique avec une méthode statique, les variables locales dans la méthode sont-elles sûres si plusieurs threads l'appellent?

dans quelles circonstances dois-je utiliser le verrouillage ou d'autres techniques spéciales de sécurité des fils pour assurer un accès sécuritaire à une variable?

vous devez le faire s'il y a deux threads, les deux peuvent accéder à la variable, au moins un des threads est en train de la muter, et au moins un des threads effectue une opération non atomique sur elle.

(je note qu'il peut y avoir d'autres facteurs en jeu. Par exemple, si vous avez besoin de valeurs constamment observées des variables de mémoire partagée vues à partir de chaque fil vous devez alors utiliser un même si les opérations sont toutes atomiques. C# ne garantit pas la cohérence séquentielle des observations de mémoire partagée même si la variable est marquée comme volatile.)

Super. Concentrons-nous sur la partie "les deux peuvent accéder à la variable". Dans quelles circonstances deux threads peuvent-ils accéder à une variable locale?

typique circonstances, une variable locale ne peut être accessible qu'à l'intérieur de la méthode qui la déclare. Chacun l'activation de la méthode créera une variable différente, et donc peu importe combien de fois la méthode est activée sur des threads différents, la même variable n'est pas accessible sur deux threads différents.

cependant, il y a des moyens d'accéder à une variable locale en dehors de la méthode qui l'a créée.

tout D'abord, un paramètre "ref" ou "out" peut être un alias d'une variable locale, mais le CLR et le langage C# ont tous deux été soigneusement conçus de sorte que les alias des variables locales sont uniquement accessibles à partir de méthodes qui ont été par la méthode déclarant la variable (directement ou indirectement). Par conséquent, cela devrait toujours être threadsafe; il ne devrait pas y avoir un moyen d'obtenir un ref d'un fil à l'autre, et ainsi partager la variable à travers les fils.

deuxièmement, une variable locale peut être une locale fermée d'une méthode lambda ou anonyme. Dans ce cas, une variable locale n'est pas nécessairement des threads. Si le délégué est stocké dans la mémoire partagée puis deux threads peuvent manipuler le local indépendamment!

static class Foo
{
    private static Func<int> f;
    public static int Bar()
    {
        if (Foo.f == null)
        {
           int x = 0;
           Foo.f = ()=>{ return x++; };
        }
        return Foo.f();
    }
}

ici " Bar "a une variable locale"x". Si Bar est appelé sur plusieurs threads, alors d'abord la course de threads pour déterminer qui définit Foo.f. L'un d'eux gagne. Et à partir de maintenant, les appels à la barre sur les fils multiples tous manipulent de manière peu sûre même variable locale x qui a été capturée par le délégué créé par le thread gagnant. Étant une variable locale n'est pas un garantie de la sécurité du fil.

Troisième, une variable locale à l'intérieur d'un bloc itérateur a le même problème:

static class Foo
{
    public static IEnumerable<int> f;
    private static IEnumerable<int> Sequence()
    {
        int x = 0;
        while(true) yield return x++;
    }
    public static Bar() { Foo.f = Sequence(); }
}

Si quelqu'un appelle Toto.Bar() et accède ensuite à Foo.f à partir de deux threads différents, encore une fois une seule variable locale x peut subir une mutation instable sur deux threads différents. (Et bien sûr les mécanismes qui commandent l'itérateur de la logique sont pas thread-safe.)

Quatrièmement, dans le code qui est marqué comme "non sécuritaire" une variable locale peut être partagé à travers les threads en partageant un pointeur vers le local à travers les threads. Si vous marquez un bloc de code comme "dangereux" alors vous sont chargés de s'assurer que le code est threadsafe s'il doit l'être. N'éteignez le système de sécurité que si vous savez ce que vous faites.

58
répondu Eric Lippert 2011-08-25 19:43:03
la source

Autres questions sur