Pregunta

Realmente agradecería algunos consejos sobre este asunto.

por ejemplo.

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

Así que yo quiero tener un mecanismo para hacer getData() flujos seguros. Tengo llegar a mi propia solución que consiste en envasar el miembro de datos en la siguiente clase de plantilla con un mutex utilizado para el acceso se sincronizan con ella. Qué piensas ? ¿Cuáles podrían ser los posibles problemas?

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;
    }
};

Gracias por los comentarios y opiniones de antelación.

Fatih

¿Fue útil?

Solución

¿Alguna vez has oído hablar de La Ley de Demeter ?

Hay un consejo similar (a partir de Sutter creo): No compartir referencias a sus componentes internos

Ambos tienen el propósito de evitar acoplamiento , ya que al compartir una referencia a su interior, esto significa que su interfaz pública se escapa un detalle de implementación.

Ahora que esto se dice, su interfaz no funciona.

El problema es que su forma se acoplan al proxy, no el objeto: todavía puedo acceder a través de múltiples caminos:

  • de Foo, sin exclusión mutua requiere -> oups
  • a partir de dos diferentes locked_object -> esto no parece intencional ...

Más importante aún, no se puede, en general, bloquear una sola parte de un objeto, porque entonces no se puede tener la semántica transaccional para el objeto en su conjunto.

Otros consejos

Sus pone de diseño a responsabilidad en el usuario para garantizar que el objeto está bloqueado y desbloqueado en el momento correcto. Y a pesar de que su objeto bloqueado no comprobación de errores, que no cubre todas las bases (como olvidar a liberar el objeto cuando haya terminado con él)

Así que digamos que usted tiene TData objeto inseguro. Se envuelve en este Foo pero en lugar de Foo devolviendo un puntero a TData, reimplementar todos los métodos públicos en TData en Foo pero el uso de bloqueos y desbloqueos.

Esto es muy similar a la href="http://en.wikipedia.org/wiki/Opaque_pointer" rel="nofollow"> pImpl patrón

Este es el problema central de multi-threading, no se puede exigir que el código de cliente utiliza su objetivo de una manera segura para los subprocesos. Tampoco se puede realmente hacer nada para ayudar a la caída código de cliente en el pozo de éxito, tiene que cuidar el bloqueo por sí mismo. Poner la responsabilidad de hacer su trabajo de código en la persona que es menos probable que hacerlo bien.

Puede que sea más fácil mediante la devolución de un copiar del objeto de su descriptor de acceso. Que de seguro para subprocesos, sólo habrá una copia propiedad de un hilo. Probablemente debería hacer tipo inmutable de esa copia a la re-inForce que la modificación del objeto no tendrá probablemente el resultado deseado. Un efecto secundario sin solución que podría morder mal es que esta copia es por definición rancio. Estas son las tiritas que pueden hacer más daño que bien.

Documento el relleno del método para que el programador cliente sabe qué hacer.

Esto no es particularmente seguro. Nada detiene al usuario de hacer las cosas fuera de orden:

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

Por supuesto, es fácil ver por qué el código anterior es malo, pero el código real es mucho más compleja, y hay muchas formas en que los punteros podría colgar alrededor después se libera el bloqueo que son mucho más difíciles de precisar.

scroll top