126 votes

Const signifie thread-safe en C ++11 ?

J’ai entendu dire que `` signifie thread-safe dans C ++11. C'est vrai ?

Cela signifie-t-il que est maintenant l’équivalent de *Java*de ?

Ils sont à court de Mots-clés?

144voto

K-ballo Points 44794

J'entends que l' const moyen des threads en C++11. Est-ce vrai?

C'est un peu vrai...

C'est ce que le Standard de la Langue a à dire sur le thread de sécurité:

[1.10/4] Deux évaluations d'expression des conflits si l'un d'eux modifie un emplacement de mémoire (1.7) et l'autre accède ou modifie le même emplacement de mémoire.

[1.10/21] L'exécution d'un programme, contient les données de la course si elle contient deux actions contradictoires dans des threads différents, dont au moins une n'est pas atomique, et ni arrive avant l'autre. Ces données de résultats de la course dans un comportement indéfini.

qui n'est rien d'autre que la condition suffisante pour des données course pour se produire:

  1. Il y a deux ou plusieurs actions en cours d'exécution dans le même temps sur une même chose; et
  2. Au moins l'un d'entre eux est une écriture.

La Bibliothèque Standard s'appuie sur ce, va un peu plus loin:

[17.6.5.9/1] Cette section spécifie les exigences que les implémentations doivent se rencontrer pour empêcher que les données des courses de (1.10). Chaque bibliothèque standard la fonction est de répondre à chaque exigence, sauf indication contraire. Les implémentations peuvent empêcher des données de courses dans les cas autres que ceux spécifiés ci-dessous.

[17.6.5.9/3] En C++ une fonction de la bibliothèque standard ne doit pas, directement ou indirectement, de modifier des objets (1.10) accessibles par les fils autres que le thread courant, sauf si les objets sont accessibles directement ou indirectement par l'intermédiaire de la fonction non-const arguments, this.

qui dans de simples mots dit qu'il s'attend à des opérations sur const objets pour être thread-safe. Cela signifie que la Bibliothèque Standard n'allons pas introduire de données de la course aussi longtemps que les opérations sur const objets de vos propres types, soit

  1. Composé entièrement de lit-qui est, il n'y a pas écrit--; ou
  2. En interne synchronise écrit.

Si cette attente n'est pas valable pour l'un de vos types, puis de l'utiliser, directement ou indirectement, ensemble avec tous les composants de la Bibliothèque Standard peut entraîner une données de course. En conclusion, const ne signifie pas "thread-safe" de la Bibliothèque Standard de point de vue. Il est important de noter que ce n'est qu'un contrat et il ne sera pas appliquée par le compilateur, si vous cassez, vous obtenez un comportement indéfini et vous êtes sur votre propre. Si const est présent ou pas, n'affectera pas la génération de code, du moins pas en ce qui concerne à des données de courses--.

Cela signifie - const est maintenant l'équivalent de Java' synchronized?

Pas de. Pas du tout...

Considérons la suite de trop simplifié de la classe représentant un rectangle:

class rect {
    int width = 0, height = 0;

public:
    /*...*/
    void set_size( int new_width, int new_height ) {
        width = new_width;
        height = new_height;
    }
    int area() const {
        return width * height;
    }
};

Les états-fonction area est thread-safe; non pas parce que son const, mais parce qu'il sont entièrement constitués d'opérations de lecture. Il n'y a pas écrit impliqués, et au moins une écriture impliqués est nécessaire pour les données de la course pour se produire. Cela signifie que vous pouvez appeler en area de autant de threads que vous voulez et vous aurez des résultats corrects de tous les temps.

Notez que cela ne signifie pas qu' rect est thread-safe. En fait, il est facile de voir comment, si un appel à l' area venait à se produire, en même temps qu'un appel à l' set_size sur rect, alors area pourrait le calcul de son résultat basé sur un vieux largeur et une hauteur (ou même sur le brouillage des valeurs).

Mais cela est bien, rect n'est pas const donc ce n'est pas même à être thread-safe , après tout. Un objet déclaré const rect, d'autre part, serait "thread-safe" depuis sans écritures sont possibles (et si vous envisagez const_cast-ing quelque chose de l'origine déclarée const , puis vous obtenez non défini-comportement et c'est tout).

Que veut dire alors?

Supposons que, pour la clarté de l'exposé-- que les opérations de multiplication sont extrêmement coûteuses et nous ferions mieux de les éviter si possible. Nous avons pu calculer l'aire que si elle est demandée, puis de les mettre en cache dans le cas où il est demandé de nouveau à l'avenir:

class rect {
    int width = 0, height = 0;

    mutable int cached_area = 0;
    mutable bool cached_area_valid = true;

public:
    /*...*/
    void set_size( int new_width, int new_height ) {
        cached_area_valid = ( width == new_width && height == new_height );
        width = new_width;
        height = new_height;
    }
    int area() const {
        if( !cached_area_valid ) {
            cached_area = width;
            cached_area *= height;
            cached_area_valid = true;
        }
        return cached_area;
    }
};

[Si cet exemple semble trop artificiel, vous pouvez remplacer mentalement int par un très grand allouée dynamiquement entier qui est intrinsèquement non thread-safe et pour qui les multiplications sont extrêmement coûteux.]

Les états-fonction area n'est plus "thread-safe", elle écrit maintenant et n'est pas synchronisé en interne. Est-ce un problème? L'appel à l' area peut se produire dans le cadre d'un constructeur par copie d'un autre objet, tel constructeur pourrait avoir été appelée par une opération sur un conteneur standard, et à ce moment la bibliothèque standard s'attend à ce que cette opération se comporter comme une lecture en ce qui concerne les données de courses. Mais nous faisons écrit!

Dès que nous avons mis un rect dans un conteneur standard , directement ou indirectement-- nous entrons dans un contrat avec la Bibliothèque Standard. Continuer à le faire, écrit dans un const la fonction tout en honorant ce contrat, nous avons besoin à l'interne de synchroniser ces écrit:

class rect {
    int width = 0, height = 0;

    mutable std::mutex cache_mutex;
    mutable int cached_area = 0;
    mutable bool cached_area_valid = true;

public:
    /*...*/
    void set_size( int new_width, int new_height ) {
        if( new_width != width || new_height != height )
        {
            std::lock_guard< std::mutex > guard( cache_mutex );

            cached_area_valid = false;
        }
        width = new_width;
        height = new_height;
    }
    int area() const {
        std::lock_guard< std::mutex > guard( cache_mutex );

        if( !cached_area_valid ) {
            cached_area = width;
            cached_area *= height;
            cached_area_valid = true;
        }
        return cached_area;
    }
};

Notez que nous avons fait l' area de la fonction thread-safe, mais l' rect n'est toujours pas thread-safe. Un appel à l' area passe en même temps qu'un appel à l' set_size pouvez toujours finir par le calcul de la valeur faux, puisque les devoirs d' width et height ne sont pas protégés par le mutex.

Si nous voulions vraiment un "thread-safe" rect, nous devrions utiliser un les primitives de synchronisation pour protéger les non thread-safe rect.

Sont-ils à court de mots-clés?

Oui, ils sont. Ils ont été à court de mots-clés depuis le premier jour.


Source: Vous ne savez pas const et mutable - Herb Sutter

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