2012-11-09 9 views
6

Rozumiem koncepcję bezpieczeństwa gwintów. Szukam porady, aby uprościć bezpieczeństwo wątków, próbując chronić pojedynczą zmienną.Bezpieczeństwo gwintów pojedynczej zmiennej

Say mam zmienną:

double aPass; 

i chcę chronić tę zmienną, więc tworzę mutex:

pthread_mutex_t aPass_lock; 

Teraz istnieją dwa dobre sposoby mogę myśleć w ten sposób ale obaj mają denerwujące wady. Pierwszym jest stworzenie bezpiecznego klasę gwintu posiadać zmienną:

class aPass { 
    public: 
     aPass() { 
      pthread_mutex_init(&aPass_lock, NULL); 
      aPass_ = 0; 
     } 

     void get(double & setMe) { 
      pthread_mutex_lock(aPass_lock); 
      setMe = aPass_ 
      pthread_mutex_unlock(aPass_lock); 
     } 

     void set(const double setThis) { 
      pthread_mutex_lock(aPass_lock); 
      aPass_ = setThis; 
      pthread_mutex_unlock(aPass_lock); 
     } 
    private: 
     double aPass_; 
     pthread_mutex_t aPass_lock; 
}; 

Teraz będzie to zachować aPass całkowicie bezpieczny, nic nie może się mylić i nigdy go dotknąć, yay! jednak spójrz na cały ten bałagan i wyobraź sobie zamieszanie podczas uzyskiwania dostępu do niego. obrzydliwy.

Innym sposobem jest uzyskanie dostępu do obu tych funkcji i upewnienie się, że blokujesz muteks przed użyciem aPass.

Ale co, jeśli ktoś nowy przyjdzie na projekt, a jeśli zapomnisz jeden raz, aby go zablokować. Nie lubię debugowania problemów z wątkami, które są trudne.

Czy istnieje dobry sposób na (przy użyciu pthreads, ponieważ muszę używać QNX, który ma mało wsparcia doładowania) Aby zablokować pojedyncze zmienne bez potrzeby dużej klasy, a to jest bezpieczniejsze, to po prostu tworzenie blokady mutex, aby przejść z nim?

+0

ta zmienna jest zmienną globalną? nie sądzę, że masz dużo innego wyboru, chyba że chcesz utworzyć ogólną klasę kontenera wątku bezpiecznego - to zaoszczędziłoby ci trochę kodu, jeśli musisz to zrobić do drugiego var. –

+0

masz na myśli jak klasę szablonową? to brzmi całkiem nieźle. –

+0

również, czy nie jest to możliwe dzięki projektowi ur do duplikowania var? apass jest klasą, więc mogę mieć tyle obiektów ile chcę –

Odpowiedz

3

Aby rozwinąć moje rozwiązanie, byłoby to coś takiego.

template <typename ThreadSafeDataType> 
class ThreadSafeData{ 
    //.... 
private: 
    ThreadSafeDataType data; 
    mutex mut; 
}; 

class apass:public ThreadSafeData<int> 

Dodatkowo, aby było wyjątkowe, najlepiej byłoby uczynić wszystkich operatorów i członków statycznymi. Aby to zadziałało trzeba użyć CRTP tj

template <typename ThreadSafeDataType,class DerivedDataClass> 
class ThreadSafeData{ 
//.... 
}; 
class apass:public ThreadSafeData<int,apass> 
+1

Witaj w SO! Proszę nie używać txt-speak w komunikacie, tutaj nie ma takiej potrzeby, a generalnie jest źle mile widziana. – GManNickG

1

Możesz utworzyć klasę, która będzie działać jako rodzajowy wrapper wokół zmiennej, synchronizując dostęp do niej.

Dodaj operatora przeciążającego dla zadania i gotowe.

+0

Jakiś przykładowy kod? Czy masz na myśli, że każdy z typów zmiennych zamierzam użyć w jednej klasie, z przeciążeniem każdej funkcji dla tego typu. Brzmi nieźle. –

+0

Można utworzyć klasę szablonów, która będzie działać z dowolnym typem danych. – thedayofcondor

+0

Zobacz kod @Karthik T ... moje C++ jest nieco zardzewiałe i nie byłoby w stanie odpisać mi głowy bez IDE ... Staję się zbyt stary – thedayofcondor

1

Rozważmy używać RAII idiom poniżej kod to tylko pomysł, to nie przetestowane:

template<typename T, typename U> 
struct APassHelper : boost::noncoypable 
{ 
    APassHelper(T&v) : v_(v) { 
    pthread_mutex_lock(mutex_); 
    } 
    ~APassHelper() { 
    pthread_mutex_unlock(mutex_); 
    } 
    UpdateAPass(T t){ 
    v_ = t; 
    } 
private: 
    T& v_; 
    U& mutex_; 
}; 

double aPass; 
int baPass_lock; 
APassHelper<aPass,aPass_lock) temp; 
temp.UpdateAPass(10); 
+0

to nadal wymaga od użytkowników ręcznego użycia helpera, którego OP chce uniknąć. –

2

Można łatwo tworzyć własne klasy, która blokuje mutex na budowie i odblokowuje go podczas destrukcji. W ten sposób, niezależnie od tego, co się stanie, muteks zostanie zwolniony przy powrocie, nawet jeśli zostanie zgłoszony wyjątek lub jakakolwiek ścieżka zostanie podjęta.

class MutexGuard 
{ 
    MutexType & m_Mutex; 
public: 

    inline MutexGuard(MutexType & mutex) 
     : m_Mutex(mutex) 
    { 
     m_Mutex.lock(); 
    }; 

    inline ~MutexGuard() 
    { 
     m_Mutex.unlock(); 
    }; 
} 


class TestClass 
{ 
    MutexType m_Mutex; 
    double m_SharedVar; 

    public: 
     TestClass() 
      : m_SharedVar(4.0) 
     { } 

     static void Function1() 
     { 
      MutexGuard scopedLock(m_Mutex); //lock the mutex 
      m_SharedVar+= 2345; 
      //mutex automatically unlocked 
     } 
     static void Function2() 
     { 
      MutexGuard scopedLock(m_Mutex); //lock the mutex 
      m_SharedVar*= 234; 
      throw std::runtime_error("Mutex automatically unlocked"); 
     } 

} 

Zmienna m_SharedVar zapewniona jest wzajemne wykluczanie pomiędzy Function1() i Function2() i będzie zawsze odblokowany po powrocie.

boost ma wbudowane typy, aby to osiągnąć: boost :: scoped_locked, boost :: lock_guard.

+1

Fajna odpowiedź, ale niestety powiedziałem na moje pytanie, że nie mogę użyć boost. W przeciwnym razie byłbym w całym zestawie zamków mutex. –

+3

Pokazał ci, jak zrobić własny blokada zasięgu –

1

Możesz modyfikować swoją klasę aPass za pomocą operatorów zamiast get/set:

class aPass { 
public: 
    aPass() { 
     pthread_mutex_init(&aPass_lock, NULL); 
     aPass_ = 0; 
    } 

    operator double() const { 
     double setMe; 
     pthread_mutex_lock(aPass_lock); 
     setMe = aPass_; 
     pthread_mutex_unlock(aPass_lock); 
     return setMe; 
    } 

    aPass& operator = (double setThis) { 
     pthread_mutex_lock(aPass_lock); 
     aPass_ = setThis; 
     pthread_mutex_unlock(aPass_lock); 
     return *this; 
    } 
private: 
    double aPass_; 
    pthread_mutex_t aPass_lock; 
}; 

Zastosowanie:

aPass a; 
a = 0.5; 
double b = a; 

Może to być oczywiście matrycy w celu wsparcia innych rodzajów. Należy jednak pamiętać, że muteks w tym przypadku jest przesadny. Ogólnie rzecz biorąc, bariery pamięciowe wystarczają, gdy chronimy ładunki i magazyny małych typów danych. Jeśli to możliwe, powinieneś użyć C++ 11 std::atomic<double>.

5
std::atomic<double> aPass; 

pod warunkiem, że masz C++ 11.

+0

niestety nie, nie mam C++ 11 –

Powiązane problemy