2010-09-03 10 views
5

Czy użycie tu tymczasowego jest bezpieczne?Czy powrót atomowy i powinienem użyć tymczasowego w gettera, aby był bezpieczny dla wątków?

int getVal() { 
     this->_mutex.lock(); 
     int result = this->_val; 
     this->_mutex.unlock(); 
     return result; 
} 

dam ci demontaż funkcji testowej prosty RAII

int test() 
{ 
    RAIITest raii; //let's say it's a scoped lock 
    return 3; 
} 


{ 
    0x004013ce <_Z4testv>: push %ebp 
    0x004013cf <_Z4testv+1>: mov %esp,%ebp 
    0x004013d1 <_Z4testv+3>: sub $0x28,%esp 
    return 3; 
    0x004013d4 <_Z4testv+6>: lea -0x18(%ebp),%eax 
    0x004013d7 <_Z4testv+9>: mov %eax,(%esp) 
    0x004013da <_Z4testv+12>: call 0x4167a0 <_ZN8RAIITestD1Ev> //here destructor is called 
    0x004013df <_Z4testv+17>: mov $0x3,%eax //here result is pushed onto the stack 
} 
0x004013e4 <_Z4testv+22>: leave 
0x004013e5 <_Z4testv+23>: ret 

kompilator gcc jest/g ++ 3.4.5

+0

::: Przypadkowo przeczytaj to jako C# i VTC. Przepraszam. – Brian

+0

Nie widzę, co twój przykład RAII ma udowodnić. Zwrócenie stałej tylko krzyczy do kompilatora, że ​​można oszukiwać i optymalizować, ponieważ nie ma to wpływu na wartość zwracaną. – jalf

Odpowiedz

5

Jeśli dostęp do this->_val jest synchronizowany przez this->_mutex, to don nie ma możliwości wyboru sposobu, w jaki kod jest obecnie zapisywany. Przed odblokowaniem muteksa musisz przeczytać this->_val i odblokować muteks przed powrotem. Zmienna result jest niezbędna do uzyskania tej kolejności działań.

Jeśli używasz lock_guard (lub scoped_lock w trybie zwiększenia), nie musisz używać tymczasowego, ponieważ blokada muteksu zostanie zwolniona po powrocie funkcji. Na przykład przy użyciu biblioteki wątków C++ 0x:

int getVal() { 
    std::lock_guard<std::mutex> lock(_mutex); 
    return this->_val; 
} // lock is released by lock_guard destructor 
+0

Nie, nie będzie. Jeśli ten -> _ val jest wynikiem wbudowanym, będzie dokładnie taki sam. Obiekt (scoped_lock) jest niszczony, zanim wynik zostanie przesunięty na stos. – doc

+2

@doc: Co nie będzie? Lokalna zmienna 'lock' nie zostanie zniszczona, dopóki nie zostanie wykonana kopia zwracanego obiektu. –

+0

przyjrzyjmy się rozwiązaniu dodałem do mojego pytania – doc

1

Tak, jeśli używasz jawnej blokady()/odblokowania(). Nie, jeśli skonstruujesz obiekt blokady na stosie i jego destruktor wykona odblokowanie.

+0

dlaczego? Obiekt jest niszczony, zanim wynik zostanie przesunięty z powodu powrotu. – doc

+0

Tak nie jest.Rozważ ten kod: std :: string f() { std :: string ret = "abc"; return ret; } Jeśli 'return' został zniszczony zanim instrukcja return skopiowała go jako wartość zwracaną przez funkcję, f() może zwrócić śmieci, na przykład, jeśli w ogóle. – usta

+0

@usta łatwiej jest wybrać typ wewnętrzny. Budowa, zniszczenie, to są warunki wysokiego poziomu. Za kulisami wynik jest zwykle pchany na stos wywoławczy funkcji zdefiniowany przez pewien ABI. A co mnie martwi, jest to, że destruktor muteksu jest wywoływany, zanim pojawi się rezultat. – doc

0

Nie - kompilator automatycznie tworzy tymczasowy dla wartości zwracanej. Normalnie nie musisz chronić odczytu z muteksem, więc nawet jeśli jego wielowątkowe, tylko return _val; powinno wystarczyć.

Na marginesie, pozbyłbym się wiodącego podkreślenia - zasady dotyczące nazw zmiennych, których można i nie można używać, gdy zaczynają się od podkreślenia, są wystarczająco złożone, aby lepiej je unikać.

+0

Jerry Twój komentarz nie wymaga blokady odczytu, ponieważ jest to int, czy to prawda? W przypadku bardziej złożonych typów nie chciałbyś, aby jednoczesne pisanie odbywało się w tym samym czasie, co tworzenie kopii, tak bym pomyślał. Dzięki. –

+0

@Steve: Tak. Oczywiście, nic w standardzie nie mówi, że tak musi być (ponieważ standard w ogóle nie uwzględnia wątków), ale z praktycznego punktu widzenia int jest zasadniczo zawsze atomowe. –

+0

ale "kopiowanie" tego tymczasowego zdarzenia ma miejsce po odblokowaniu muteksa. Jeśli wtedy wątek zostanie zawieszony, a drugi wątek zmodyfikuje '_val' (umieszczam w tym przykładzie podkreślenia, aby wskazać, że jest członkiem klasy (ale potem dodałem' this-> ', aby jeszcze lepiej wskazać) więc wybaczcie mi) podczas "kopiowania", wtedy wynik może potencjalnie stać się rubish. Wiem, że w praktyce tak się nie stanie, ale chciałbym się dowiedzieć, co jest w pełni wątkowe. – doc

0

Można to zrobić czysto to mutex jest zamknięty w scoped_lock że odblokowuje na zniszczenia:

int getVal() { 
     scoped_lock lockit(_mutex); 
     return _val; 
} 

I tak, trzeba zrobić, aby utrzymać blokadę dopóki nie wrócił.

+0

W przypadku typu _val, który jest wbudowany, demontaż będzie wyglądał dokładnie tak samo, jak w przypadku zamków o nierozpoznanym zakresie – doc

Powiązane problemy