web-dev-qa-db-fra.com

Les threads des tableaux C # sont-ils sûrs?

En particulier

  1. Créez une fonction pour prendre un tableau et un index comme paramètres.
  2. Créez un tableau à n éléments.
  3. Créez une boucle de nombre n.
  4. À l'intérieur de la boucle sur un nouveau thread, affectez une nouvelle instance de l'objet au tableau à l'aide de l'indexeur transmis.

Je sais comment gérer les threads, etc. Je suis intéressé de savoir si c'est un moyen sûr de faire quelque chose.

 class Program
{
    // bogus object
    class SomeObject
    {
        private int value1;
        private int value2;

        public SomeObject(int value1, int value2)
        {
            this.value1 = value1;
            this.value2 = value2;
        }
    }

    static void Main(string[] args)
    {

        var s = new SomeObject[10];
        var threads = Environment.ProcessorCount - 1;
        var stp = new SmartThreadPool(1000, threads, threads);
        for (var i = 0; i < 10; i++)
        {
            stp.QueueWorkItem(CreateElement, s, i);
        }

    }

    static void CreateElement(SomeObject[] s, int index)
    {
        s[index] = new SomeObject(index, 2);
    }
}
51
Gary

Je crois que si chaque thread ne fonctionne que sur une partie distincte du tableau, tout ira bien. Si vous allez partager les données (c'est-à-dire les communiquer entre les threads), vous aurez besoin d'une sorte de barrière de mémoire pour éviter les problèmes de modèle de mémoire.

I croyez que si vous générez un tas de threads, chacun remplissant sa propre section du tableau, attendez que tous ces threads finissent d'utiliser Thread.Join, que cela suffira en termes de barrières pour que vous soyez en sécurité. Je n'ai pas de documentation à l'appui pour le moment, remarquez ...

EDIT: votre exemple de code est sûr. A aucun moment, deux threads n'accèdent au même élément - c'est comme s'ils avaient chacun des variables distinctes. Cependant, cela n'a pas tendance à être utile en soi. À un certain point, normalement, les threads voudront partager l'état - un thread voudra lire ce qu'un autre a écrit. Sinon, cela ne sert à rien d'écrire dans un tableau partagé plutôt que dans leurs propres variables privées. C'est le point auquel vous devez faire attention - la coordination entre les threads.

44
Jon Skeet

documentation MSDN sur les tableaux dit:

Les membres publics statiques (partagés en Visual Basic) de ce type sont thread-safe. Les membres d'instance ne sont pas garantis pour être thread-safe.

Cette implémentation ne fournit pas de wrapper synchronisé (thread-safe) pour un tableau; cependant, les classes .NET Framework basées sur Array fournissent leur propre version synchronisée de la collection à l'aide de la propriété SyncRoot.

L'énumération via une collection n'est pas intrinsèquement une procédure thread-safe. Même lorsqu'une collection est synchronisée, d'autres threads peuvent toujours modifier la collection, ce qui oblige l'énumérateur à lever une exception. Pour garantir la sécurité des threads pendant l'énumération, vous pouvez soit verrouiller la collection pendant toute l'énumération, soit intercepter les exceptions résultant des modifications apportées par d'autres threads.

Donc non, ils ne sont pas sûrs pour les threads.

25
Joren

Généralement, lorsqu'une collection est dite "non threadsafe", cela signifie que les accès simultanés peuvent échouer en interne (par exemple, il n'est pas sûr de lire le premier élément de List <T> tandis qu'un autre thread ajoute un élément à la fin de la liste: la List <T> peut redimensionner le tableau sous-jacent et l'accès en lecture peut aller au nouveau tableau avant que les données n'y soient copiées).

De telles erreurs sont impossibles avec les tableaux car les tableaux sont de taille fixe et n'ont pas de tels "changements de structure". Un tableau à trois éléments n'est pas plus ou moins thread-safe que trois variables.

La spécification C # ne dit rien à ce sujet; mais il est clair si vous connaissez IL et lisez la spécification CLI - vous pouvez obtenir une référence gérée (comme celles utilisées pour les paramètres C # "ref") à un élément à l'intérieur d'un tableau, puis faire des charges et des stockages normaux et volatils. La spécification CLI décrit les garanties de sécurité des threads pour ces charges et magasins (par exemple, atomicité pour les éléments <= 32 bits)

Donc, si je ne comprends pas bien votre question, vous souhaitez remplir un tableau à l'aide de différents threads, mais n'attribuer à chaque élément du tableau qu'une seule fois? Si oui, c'est parfaitement thread-safe.

14
Daniel

L'exemple que vous fournissez est très similaire à la façon dont les propres extensions parallèles de Microsoft à C # 4.0 fonctionnent.

Ceci pour la boucle:

for (int i = 0; i < 100; i++) { 
  a[i] = a[i]*a[i]; 
}

devient

Parallel.For(0, 100, delegate(int i) { 
  a[i] = a[i]*a[i]; 
});

Donc, oui, votre exemple devrait être OK. Voici un ancien article de blog sur le nouveau support parallèle en C #.

5
Eric