Domanda

Vorrei davvero apprezzare qualche consiglio su questa materia.

per es.

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

Quindi, io voglio avere un meccanismo per rendere getData() thread-safe. Sono venuto con la mia propria soluzione che prevede l'imballaggio del membro di dati nella seguente classe template con un mutex utilizzato per l'accesso ad esso sincronizzazione. Cosa pensi ? Quali potrebbero essere i possibili problemi?

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

Grazie per eventuali commenti e opinioni di anticipo.

Fatih

È stato utile?

Soluzione

Avete mai sentito parlare di La Legge di Demetra ?

C'è un consiglio simile (da Sutter credo): non condividono i riferimenti ai vostri interni

Entrambi hanno lo scopo di evitare accoppiamento perché condividendo un riferimento alla interna, significa che l'interfaccia pubblica perde un dettaglio di implementazione.

Ora che questo è detto, l'interfaccia non funziona.

Il problema è che il vostro stanno Blocco del proxy, non l'oggetto: è comunque possibile accedere attraverso percorsi multipli:

  • da Foo, senza mutex richiesto -?> Oups
  • di due diversi locked_object -> questo non sembra intenzionale ...

Ancora più importante, non è possibile, in generale, bloccare una singola parte di un oggetto, perché allora non si può avere semantica transazionale per l'oggetto nel suo complesso.

Altri suggerimenti

I tuoi mette design nei onere per l'utente al fine di garantire che l'oggetto è bloccato e sbloccato al momento giusto. E anche se il vostro oggetto bloccato non il controllo degli errori, non copre tutte le basi (come dimenticare di rilasciare l'oggetto quando finito con esso)

Quindi, consente di dire che avete TData oggetto non sicuro. Si avvolgono questo Foo ma invece di Foo restituire un puntatore a TData, reimplementare tutti i metodi pubblici in TData in Foo ma utilizzando blocca e sblocca.

Questo è molto simile al href="http://en.wikipedia.org/wiki/Opaque_pointer" rel="nofollow"> Pimpl modello

Questo è il problema centrale del multi-threading, non si può pretendere che il codice client utilizza l'oggetto in un modo thread-safe. Né si può davvero fare qualcosa per aiutare il codice del client caduta nella fossa di successo, deve prendersi cura del bloccaggio di per sé. Mettere l'onere di rendere il vostro lavoro di codice sulla persona che è meno probabile che farlo bene.

Si può rendere più facile restituendo un copiare dell'oggetto dal vostro accesso. Quella di thread-safe, ci sarà solo una copia di proprietà di un thread. Probabilmente dovrebbe fare quel tipo di copia immutabile di ri-Inforce che modifica l'oggetto non sarà probabilmente il risultato desiderato. Un irrisolvibile effetto collaterale che potrebbe mordere male è che la copia è per definizione stantio. Si tratta di cerotti che possono fare più male che bene.

Documento il ripieno fuori del metodo in modo che il programmatore cliente sa cosa fare.

Questo non è particolarmente sicuro. Nulla impedisce all'utente di fare le cose in ordine:

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

Naturalmente, è facile capire perché il codice di cui sopra è male, ma il codice reale è molto più complessa, e ci sono molti modi in cui i puntatori possono appendere in giro dopo il blocco viene rilasciato che sono molto più difficili da definire.

scroll top