Question

J'apprécierais vraiment quelques conseils à ce sujet.

par exemple.

class Foo
{
    TData data;
public:
    TData *getData() { return &data; } // How can we do this in a thread safe manner ?
};

Alors je veux avoir un mécanisme pour faire getData() thread-safe. Je suis venu avec ma propre solution qui consiste à emballer le membre de données dans la classe de modèle suivant avec un mutex utilisé pour l'accès synchronisent. Qu'est-ce que tu penses ? Quelles pourraient être les éventuels problèmes?

class locked_object : boost::noncopyable
{
    T *object;
    TLockable *lock;
    bool locked;

public:
    locked_object(T *_object, TLockable *_lock) : object(_object), lock(_lock), locked(true)
    {
        lock->lock();
    }

    ~locked_object()
    {
        lock->unlock();
    }

    T *get()
    {
        _ASSERT(locked);
        if (!locked)
            throw new std::exception("Synchronization error ! Object lock is already released !");
        return this->tobject;
    }


    void unlock()
    {
        locked = false;

        lock->unlock();
    }

    T *operator ->() const
    {   
        _ASSERT(locked);
        if (!locked)
            throw new std::exception("Synchronization error ! Object lock is already released !");

        return this->tobject; 
    }

    operator T *() const
    {
        _ASSERT(locked);
        if (!locked)
            throw new std::exception("Synchronization error ! Object lock is already released !");

        return this->tobject;
    }
};

Merci pour tous les commentaires et opinions à l'avance.

Fatih

Était-ce utile?

La solution

Avez-vous déjà entendu parler de La loi de Déméter ?

Il y a un conseil similaire (de Sutter je pense): Ne pas partager les références à vos internes

Les deux sont destinés à éviter couplage , car en partageant une référence à votre interne, cela signifie que votre interface publique fuites un détail de mise en œuvre.

Maintenant que cela est dit, l'interface ne fonctionne pas.

Le problème est que votre verrouillez le proxy, et non l'objet: je peux encore l'accès par plusieurs chemins:

  • de Foo, pas mutex nécessaire -> oups
  • de deux locked_object différents -> cela ne semble pas intentionnel ...

Plus important encore, vous ne pouvez pas, en général, verrouiller une seule partie d'un objet, parce que vous ne pouvez pas avoir la sémantique transactionnelle pour l'objet dans son ensemble.

Autres conseils

Vos puts conception à charge de la preuve sur l'utilisateur de veiller à ce que l'objet est verrouillé et déverrouillé au bon moment. Et même si votre objet verrouillé ne contrôle d'erreur, il ne couvre pas toutes les bases (comme oublier de libérer l'objet lorsque vous avez terminé avec elle)

Disons que vous avez TData des objets non sûrs. Vous enveloppez ceci dans Foo mais au lieu de Foo retourner un pointeur vers TData, réimplémentez toutes les méthodes publiques de TData en Foo mais en utilisant les verrous et déverrouille.

Ceci est très similaire à la Pimpl modèle sauf votre interface ajoute les verrous avant d'appeler la mise en œuvre. De cette façon, l'utilisateur sait que l'objet est sûr de fil et n'a pas besoin de vous soucier de la synchronisation.

Ceci est le problème central de multi-threading, vous ne pouvez pas exiger que le code client utilise votre objet de manière thread-safe. Et vous ne pouvez vraiment faire quelque chose pour aider la chute du code client dans la fosse de succès, il doit prendre en charge le verrouillage par lui-même. Mettre la charge de faire votre travail de code sur la personne qui est moins susceptible de l'obtenir droit.

Vous pouvez le rendre plus facile en retournant un copier de l'objet de votre accesseur. C'est thread-safe, il y aura seulement une copie détenue par un fil. Vous devriez probablement faire l'immuable type de cette copie à refaire le plein d'que la modification de l'objet ne sera pas probablement le résultat souhaité. Un effet secondaire sans solution qui pourrait mordre mal est que cette copie est par définition rassis. Ce sont des sparadraps qui sont susceptibles de faire plus de mal que de bien.

Document de la farce de la méthode afin que le programmeur client sait ce qu'il faut faire.

Ce n'est pas particulièrement sûr. Rien n'empêche l'utilisateur de faire avancer les choses hors d'usage:

locked_object<T> o(...);
T* t = o.get();
o.unlock();
t->foo(); // Bad!

Bien sûr, il est facile de voir pourquoi le code ci-dessus est mauvais, mais le code réel est beaucoup plus complexe, et il y a plusieurs façons dont les pointeurs pourraient traîner après le verrou est libéré qui sont beaucoup plus difficiles à cerner.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top