|
#1
|
|
|
|
|
W ramach prywatnych cwiczen sprawnosci umyslowej napisalem co ponizej.
Zadanie dla chetnych to: 1. odgadnac co ten kod robi i do czego moze byc przydatny 2. (opcjonalne) znalezc i poprawic bledy 3. zmienic na jedno (albo oba) z a) (opcjonalne) poprawny b) przenosny c) prostszy d) szybszy *) Nagroda to wlasna satysfakcja i dopisanie do Copyright. Im wyzej na liscie a) b) c) d), tym satyfakcja powinna byc wieksza. Program musi dzialac na obu platformach sprzetowych : ia32 oraz x64. *)Poprawienie wydajnosci liczy sie tylko w jednej sciezce wykonania : uzyskanie dostepu do wczesniej utworzonego obiektu. Inne nie musza byc optymalne. B. // Copyright (C) 2009, Bronislaw (Bronek) Kozicki // // Use, modification, and distribution is subject to the Boost Software // License, Version 1.0. , see http://www.boost.org/LICENSE_1_0.txt namespace util { template <typename T> class single_ptr { enum {Absent, Coming, Present, Dying}; static __declspec(align(8)) volatile long ref_counter_; static __declspec(align(8)) volatile long ref_state_; static __declspec(align(8)) const T * volatile ref_impl_; const T * impl_; public: single_ptr() { for(;;) { if (_InterlockedIncrement(&ref_counter_) == 1) { while (_InterlockedCompareExchange(&ref_state_, Coming, Absent) != Absent) ::Sleep(1); try { impl_ = new T(); } catch(...) { _InterlockedExchange(&ref_state_, Absent); _InterlockedDecrement(&ref_counter_); throw; } InterlockedExchangePointer((void* volatile*) &ref_impl_, (void*)impl_); _InterlockedExchange(&ref_state_, Present); break; } else { if ( ::InterlockedCompareExchange(&ref_state_, Present, Present) == Present && ( impl_ = (const T*)InterlockedCompareExchangePointer((void* volatile*) &ref_impl_, 0, 0) ) != NULL ) break; _InterlockedDecrement(&ref_counter_); ::Sleep(1); } } } const T * operator-> () const { return impl_; } const T& operator* () const { return *impl_; } ~single_ptr() { if (_InterlockedDecrement(&ref_counter_) == 0) { while (_InterlockedCompareExchange(&ref_state_, Dying, Present) != Present) ::Sleep(1); InterlockedExchangePointer((void* volatile*) &ref_impl_, 0); _InterlockedExchange(&ref_state_, Absent); delete impl_; } } }; template <typename T> __declspec(align(8)) volatile long single_ptr<T>::ref_counter_ = 0; template <typename T> __declspec(align(8)) volatile long single_ptr<T>::ref_state_ = single_ptr<T>::Absent; template <typename T> __declspec(align(8)) const T * volatile single_ptr<T>::ref_impl_ = NULL; } |
|
|
|
#2
|
|
|
|
|
Bronek Kozicki pisze:
> W ramach prywatnych cwiczen sprawnosci umyslowej napisalem co ponizej. > Zadanie dla chetnych to: > > 1. odgadnac co ten kod robi i do czego moze byc przydatny Singelton LF. > > 2. (opcjonalne) znalezc i poprawic bledy Pomiedzy Coming a Present 1-ki mija czas w ktorym moze zostac utworzona instancja w innym watku ktora wejdzie w else a na warunku if( ::InterlockedCompareExchange(&ref_state_, Present, Present) == Present nie wejdzie w break wiec zrobi _InterlockedDecrement(&ref_counter_); Bediesz miec 2 instancje opakowan i licznik 1 a destruktor zawsze zmniejsza licznik, wiec wyglada na to ze moze wystapic delete 0; 1 instancja Coming 2 instancja else Decrement(&ref_counter_); 1 instancja Present 2 ~single_ptr() 2 if (Decrement(&ref_counter_) == 0) 2 Exchange(&ref_state_, Dying, Present) 2 delete impl_; // delete 0 po co 'Sleep' po _InterlockedDecrement(&ref_counter_); ? Czyli w ogolnosci niezaleznie od konstrukcji kazda instancja zmniejsza licznik w destruktorze mimo iz moze zrobic to w konstruktorze gdy sie jej nie uda. ~single_ptr() if (_InterlockedDecrement(&ref_counter_) == 0) Dzis pozno a jutro mam laborki rano wiec wiecej nie napisze, ale zadanie fajne zmusza do myslenia, wydaje mi sie ze zamotales troche tj przynajmiej w momencie gdy czas zycia otoczek bedzie krótki. Co do przenosnosci, nie uzywam bezposrednio Interlocked a pisze funkcje inline cas( where , old_val, new_val ) z kolejnoscia zgodna z przyjeta nomenklatura czyli inna niz ma M$ a te customizuje zaleznie od gcc, msvc etc. http://gcc.gnu.org/onlinedocs/gcc-4....-Builtins.html NP: #if defined(WIN32) #pragma intrinsic (_InterlockedCompareExchange64) #pragma intrinsic (_InterlockedCompareExchange) #pragma intrinsic (_InterlockedIncrement) #pragma intrinsic (_InterlockedDecrement) #pragma intrinsic (_InterlockedExchangeAdd) extern "C" { long _InterlockedIncrement( volatile long * ptr ); long _InterlockedDecrement( volatile long * ptr ); long _InterlockedOr( volatile long * ptr, long value ); long _InterlockedCompareExchange( volatile long * dst, long new_val, long old_val ); __int64 _InterlockedCompareExchange64( volatile __int64 * dst, __int64 new_val, __int64 old_val ); } #endif namespace mt { #if !defined(WIN32) typedef int32_t mt_int32_t; inline int32_t interlocked_increment( volatile int32_t * ptr ) { return __sync_fetch_and_add ( ptr, 1); } inline int32_t interlocked_decrement( volatile int32_t * ptr ) { return __sync_fetch_and_sub ( ptr, 1); } inline int64_t cas( volatile int64_t * loc, int64_t comparand, int64_t value ){ return __sync_val_compare_and_swap( loc, comparand, value); } inline int32_t cas( volatile int32_t * loc, int32_t comparand, int32_t value ){ return __sync_val_compare_and_swap( loc, comparand, value); } inline int32_t interlocked_add ( volatile int32_t * ptr, int32_t value ) { return __sync_fetch_and_add ( ptr, value ); } #else //_MSC_VER typedef long mt_int32_t; inline long interlocked_increment( volatile long * ptr ) { return _InterlockedIncrement ( ptr ); } inline long interlocked_decrement( volatile long * ptr ) { return _InterlockedDecrement ( ptr ); } inline __int64 cas( volatile __int64 * loc, __int64 comparand, __int64 value ){ return _InterlockedCompareExchange64( loc, value, comparand); } inline long cas( volatile long * loc, long comparand, long value ){ return _InterlockedCompareExchange( loc, value, comparand); } inline long interlocked_add ( volatile long * ptr, long value ) { return _InterlockedExchangeAdd( ptr, value ); } #endif } A. |
|
#3
|
|
|
|
|
Bronek Kozicki wrote:
> a) (opcjonalne) poprawny Slabo znam sie na exchange, ale zaryzykuje taka wapliwosc, przynajmniej sie czegos naucze: Watek A wykonuje fragment: ~single_ptr() { if (_InterlockedDecrement(&ref_counter_) == 0) { Watek B: single_ptr() .... if (::InterlockedCompareExchange(&ref_state_, Present, Present) == Present && ( impl_ = (const T*)InterlockedCompareExchangePointer((void* volatile*) &ref_impl_, 0, 0) ) != NULL Watek A (kontynuacja ~single_ptr()): InterlockedExchangePointer((void* volatile*) &ref_impl_, 0); _InterlockedExchange(&ref_state_, Absent); delete impl_; Watek B wychodzi z konstruktora i ma skasowany pointer w impl_ - BUM. Licze na lagodny wymiar kary :D |
|
#4
|
|
|
|
|
On 28/11/2009 23:50, Sebastian Bialy wrote:
[..] >> > Watek A (kontynuacja ~single_ptr()): > > InterlockedExchangePointer((void* volatile*) &ref_impl_, 0); > _InterlockedExchange(&ref_state_, Absent); > > delete impl_; > nie grozi. watek A. ~single_ptr() { if (_InterlockedDecrement(&ref_counter_) == 0) tutaj licznik zostal atomowo zmniejszony. watek B. single_ptr() { for(;;) { if (_InterlockedIncrement(&ref_counter_) == 1) { jezeli wykonalo sie *przed* zmniejszeniem licznika w destruktorze, to wynikiem zmniejszenia w destruktorze nie jest zero, a wiec delete sie nie wykona. Jezeli wykonalo sie *po* zmniejszeniu w destruktorze (które zwrócilo 0, powodujace delete) to wynikiem zwiekszenia jest 1, a wiec wykona sie new. Kolejnosc wykonania new i delete jest regulowana przez czekanie na stan "present" w destruktorze , oraz na stan "absent" w konstruktorze. Ten drugi jest ustawiony statycznie oraz w destruktorze , po wyzerowaniu wskaznika na implementacje. To znaczy ze wskaznik najpierw musi zostac wyzerowany, zanim konstruktor ustawi go znowu. Ale kod ma inny, dosyc oczywisty, blad - brak konstruktora kopiujacego. Ponizej poprawiona i nieco uproszczona wersja B. // Copyright (C) 2009, Bronislaw (Bronek) Kozicki // // Use, modification, and distribution is subject to the Boost Software // License, Version 1.0. , see http://www.boost.org/LICENSE_1_0.txt namespace util { template <typename T> class single_ptr { enum {Empty, Alive, Trans}; static __declspec(align(8)) volatile long ref_counter_; static __declspec(align(8)) volatile long ref_state_; static const T * ref_impl_; public: single_ptr() { for(;;) { if (_InterlockedIncrement(&ref_counter_) == 1) { while (_InterlockedCompareExchange(&ref_state_, Trans, Empty) != Empty) ::Sleep(1); const T* impl = NULL; try { impl = new T(); } catch(...) { _InterlockedExchange(&ref_state_, Empty); _InterlockedDecrement(&ref_counter_); throw; } ref_impl_ = impl; _InterlockedExchange(&ref_state_, Alive); break; } else { if (_InterlockedCompareExchange(&ref_state_, Alive, Alive) == Alive) break; _InterlockedDecrement(&ref_counter_); ::Sleep(1); } } } single_ptr(const single_ptr& src) { _InterlockedIncrement(&ref_counter_); } // No need to declare = , default generated is just fine // single_ptr& operator=(const single_ptr& src); const T * operator-> () const { return ref_impl_; } const T& operator* () const { return *ref_impl_; } ~single_ptr() { if (_InterlockedDecrement(&ref_counter_) == 0) { while (_InterlockedCompareExchange(&ref_state_, Trans, Alive) != Alive) ::Sleep(1); const T* impl = ref_impl_; ref_impl_ = NULL; _InterlockedExchange(&ref_state_, Empty); delete impl; } } }; template <typename T> __declspec(align(8)) volatile long single_ptr<T>::ref_counter_ = 0; template <typename T> __declspec(align(8)) volatile long single_ptr<T>::ref_state_ = single_ptr<T>::Empty; template <typename T> const T * single_ptr<T>::ref_impl_ = NULL; } |
|
#5
|
|
|
|
|
On 28/11/2009 23:43, arturbac wrote:
> Bronek Kozicki pisze: > > Singelton LF. >> Pomiedzy Coming a Present 1-ki mija czas w ktorym moze zostac utworzona > instancja w innym watku ktora wejdzie w else a na warunku > if( ::InterlockedCompareExchange(&ref_state_, Present, Present) == Present > nie wejdzie w break wiec zrobi _InterlockedDecrement(&ref_counter_); zgadza sie , a parze do InterlockedIncrement na poczatku petli. Drugi watek nie zrobi drugiej instancji, druga instancja zostanie utworzona tylko jezeli wynikiem atomowego zwiekszenia jest 1. > 1 instancja Coming ta instancja ustawila licznik na 1 > 2 instancja else Decrement(&ref_counter_); ta instancja ustawila wczesniej licznik na 2, a teraz z powrotem na 1 przed powtórzeniem iteracji > 1 instancja Present acha, czyli wskaznik juz jest ustawiony > 2 ~single_ptr() zaraz, a kiedy 2 wyszlo z konstruktora? Bo jezeli wyszlo to licznik byl 2. > po co 'Sleep' po _InterlockedDecrement(&ref_counter_); ? czekanie az inny watek ustawi Present. Pozyteczne jezeli masz jedna jednostke wykonania (CPU z 1 core, albo po prostu bardzo zajety watkami o wyzszym priorytecie) i jezeli watek ustawiajacy Present ma *nizszy* priorytet od czekajacego na Present. > Czyli w ogolnosci niezaleznie od konstrukcji kazda instancja zmniejsza > licznik w destruktorze mimo iz moze zrobic to w konstruktorze gdy sie > jej nie uda. przeczytaj uwaznie (czy przeoczyles petle w konstruktorze?), moze te nowsza uproszczona wersje, w innym moim poscie. Pamietaj ze kazda instrukcja Interlocked* wymusza pelna (load i release) bariere pamieci. B. |
|
#6
|
|
|
|
|
Mea culpa przeoczylem petle.
|
|
#7
|
|
|
|
|
On 29/11/2009 12:36, Bronek Kozicki wrote:
> On 28/11/2009 23:50, Sebastian Bialy wrote: >> Bronek Kozicki wrote: i jeszcze dodana obsluga typów bez domyslnego konstruktora (z pomoca type erasure). Trzeba wykonac init() zanim mozna bedzie utworzyc pierwszy obiekt - przyklady na koncu. Mozna stosowac zamiast singletonów ;) B. PS. ten kod jest dla zabawy. Jezeli ktos musi go na powaznie stosowac w projekcie, to moze bedzie dzialal, a moze nie. Zaleznie od wysilku wlozonego w jego zrozumienie oraz szczescia. Signletony to brudny biznes i takie pozostaja, nawet jezeli je ladnie opakowac i troche ograniczyc. PS2. Jezeli do deklaracji statycznych skladowych dopisac __declspec(thread), to zrobi sie "single_tls_ptr". Byc moze bardziej pozyteczny. // Copyright (C) 2009, Bronislaw (Bronek) Kozicki // // Use, modification, and distribution is subject to the Boost Software // License, Version 1.0. , see http://www.boost.org/LICENSE_1_0.txt // // NO WARRANTY OF ANY KIND, EITHER IMPLIED OR EXPRESS, ETC. ETC. // #include <stdexcept> namespace util { template <typename T> class single_ptr { enum {Undef, Trans1, Empty, Trans2, Alive}; static __declspec(align(8)) volatile long ref_counter_; static __declspec(align(8)) volatile long ref_state_; static const T * ref_impl_; typedef const T* (*acquire_t)(const void*); typedef void (*release_t)(const void*, const T*); static acquire_t acquire_; static release_t release_; static const void* parameter_; template <typename Def> static const T* acquire(const void* p) { Def* dp = (Def *)p; return dp->acquire(); } template <typename Def> static void release(const void* p, const T* tp) { const Def* dp = (Def *)p; dp->release(tp); } struct def_default { const T* acquire() const {return new T();} void release(const T* p) const {delete p;} }; template <typename Arg1> struct def_arg1 { const Arg1 arg1; const T* acquire() const {return new T(arg1);} void release(const T* p) const {delete p;} }; template <typename Arg1, typename Arg2> struct def_arg2 { const Arg1 arg1; const Arg2 arg2; const T* acquire() const {return new T(arg1, arg2);} void release(const T* p) const {delete p;} }; template <typename Arg1, typename Arg2, typename Arg3> struct def_arg3 { const Arg1 arg1; const Arg2 arg2; const Arg3 arg3; const T* acquire() const {return new T(arg1, arg2, arg3);} void release(const T* p) const {delete p;} }; template <typename Arg1, typename Arg2, typename Arg3, typename Arg4> struct def_arg4 { const Arg1 arg1; const Arg2 arg2; const Arg3 arg3; const Arg4 arg4; const T* acquire() const {return new T(arg1, arg2, arg3, arg4);} void release(const T* p) const {delete p;} }; template <typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5> struct def_arg5 { const Arg1 arg1; const Arg2 arg2; const Arg3 arg3; const Arg4 arg4; const Arg5 arg5; const T* acquire() const {return new T(arg1, arg2, arg3, arg4, arg5);} void release(const T* p) const {delete p;} }; public: struct error : std::logic_error { enum {Undef, Redef}; explicit error(int c) : logic_error(c == Undef ? "init() has not been called" : "init() called more than once") , code(c) {} const int code; }; single_ptr() { for(;;) { if (_InterlockedIncrement(&ref_counter_) == 1) { for (;;) { const int e = _InterlockedCompareExchange(&ref_state_, Trans2, Empty); if (e == Empty) break; else if (e == Undef) throw error(error::Undef); ::Sleep(1); } const T* impl = NULL; try { impl = acquire_(parameter_); } catch(...) { _InterlockedExchange(&ref_state_, Empty); _InterlockedDecrement(&ref_counter_); throw; } ref_impl_ = impl; _InterlockedExchange(&ref_state_, Alive); break; } else { if (_InterlockedCompareExchange(&ref_state_, Alive, Alive) == Alive) break; _InterlockedDecrement(&ref_counter_); ::Sleep(1); } } } single_ptr(const single_ptr& src) { _InterlockedIncrement(&ref_counter_); } // No need to declare = , default generated is just fine // single_ptr& operator=(const single_ptr& src); const T * operator-> () const { return ref_impl_; } const T& get() const { return *ref_impl_; } const T& operator* () const { return get(); } static void init() { if (_InterlockedCompareExchange(&ref_state_, Trans1, Undef) != Undef) throw error(error::Redef); static const def_default d; acquire_ = &acquire<def_default>; release_ = &release<def_default>; parameter_ = (const void*) &d; _InterlockedExchange(&ref_state_, Empty); } template <typename Arg1> static void init(const Arg1& a1) { if (_InterlockedCompareExchange(&ref_state_, Trans1, Undef) != Undef) throw error(error::Redef); static const def_arg1<Arg1> d = {a1}; acquire_ = &acquire<def_arg1<Arg1> >; release_ = &release<def_arg1<Arg1> >; parameter_ = (const void*) &d; _InterlockedExchange(&ref_state_, Empty); } template <typename Arg1, typename Arg2> static void init(const Arg1& a1, const Arg2& a2) { if (_InterlockedCompareExchange(&ref_state_, Trans1, Undef) != Undef) throw error(error::Redef); static const def_arg2<Arg1, Arg2> d = {a1, a2}; acquire_ = &acquire<def_arg2<Arg1, Arg2> >; release_ = &release<def_arg2<Arg1, Arg2> >; parameter_ = (const void*) &d; _InterlockedExchange(&ref_state_, Empty); } template <typename Arg1, typename Arg2, typename Arg3> static void init(const Arg1& a1, const Arg2& a2, const Arg3& a3) { if (_InterlockedCompareExchange(&ref_state_, Trans1, Undef) != Undef) throw error(error::Redef); static const def_arg3<Arg1, Arg2, Arg3> d = {a1, a2, a3}; acquire_ = &acquire<def_arg3<Arg1, Arg2, Arg3> >; release_ = &release<def_arg3<Arg1, Arg2, Arg3> >; parameter_ = (const void*) &d; _InterlockedExchange(&ref_state_, Empty); } template <typename Arg1, typename Arg2, typename Arg3, typename Arg4> static void init(const Arg1& a1, const Arg2& a2, const Arg3& a3, const Arg4& a4) { if (_InterlockedCompareExchange(&ref_state_, Trans1, Undef) != Undef) throw error(error::Redef); static const def_arg4<Arg1, Arg2, Arg3, Arg4> d = {a1, a2, a3, a4}; acquire_ = &acquire<def_arg4<Arg1, Arg2, Arg3, Arg4> >; release_ = &release<def_arg4<Arg1, Arg2, Arg3, Arg4> >; parameter_ = (const void*) &d; _InterlockedExchange(&ref_state_, Empty); } template <typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5> static void init(const Arg1& a1, const Arg2& a2, const Arg3& a3, const Arg4& a4, const Arg5& a5) { if (_InterlockedCompareExchange(&ref_state_, Trans1, Undef) != Undef) throw error(error::Redef); static const def_arg5<Arg1, Arg2, Arg3, Arg4, Arg5> d = {a1, a2, a3, a4, a5}; acquire_ = &acquire<def_arg5<Arg1, Arg2, Arg3, Arg4, Arg5> >; release_ = &release<def_arg5<Arg1, Arg2, Arg3, Arg4, Arg5> >; parameter_ = (const void*) &d; _InterlockedExchange(&ref_state_, Empty); } template <typename Def> static void init(const Def* pd) { if (_InterlockedCompareExchange(&ref_state_, Trans1, Undef) != Undef) throw error(error::Redef); acquire_ = &acquire<Def>; release_ = &release<Def>; parameter_ = (const void*) pd; _InterlockedExchange(&ref_state_, Empty); } ~single_ptr() { if (_InterlockedDecrement(&ref_counter_) == 0) { while (_InterlockedCompareExchange(&ref_state_, Trans2, Alive) != Alive) ::Sleep(1); const T* impl = ref_impl_; ref_impl_ = NULL; _InterlockedExchange(&ref_state_, Empty); release_(parameter_, impl); } } }; template <typename T> __declspec(align(8)) volatile long single_ptr<T>::ref_counter_ = 0; template <typename T> __declspec(align(8)) volatile long single_ptr<T>::ref_state_ = single_ptr<T>::Undef; template <typename T> const T * single_ptr<T>::ref_impl_ = NULL; template <typename T> typename single_ptr<T>::acquire_t single_ptr<T>::acquire_ = NULL; template <typename T> typename single_ptr<T>::release_t single_ptr<T>::release_ = NULL; template <typename T> const void* single_ptr<T>::parameter_ = NULL; } /////// sample use struct Test : boost::noncopyable { const std::string str; Test(std::string::size_type i, char t, const std::string& before, const std::string& after, char t2) : str(std::string(i, t) + before + after + t2 + std::string(i, t)) {} }; class IGulp : boost::noncopyable { public: virtual const std::string& how() const = 0; virtual ~IGulp() {} }; class Gulp : public IGulp { explicit Gulp(const std::string& t) : str(t) {} friend class Throat; const std::string str; public: virtual const std::string& how() const {return str;} }; class Throat : boost::noncopyable { std::string str; public: explicit Throat(const char* t) : str(t) {} const IGulp* acquire() const {return new Gulp(str);} void release(const IGulp* p) const {delete p;} }; int main(int argc, char** argv) { util::single_ptr<double>::init(); util::single_ptr<double> d; std::printf("%f\n", *d); util::single_ptr<int>::init(42); util::single_ptr<int> i; std::printf("%i\n", *i); util::single_ptr<Test>::init(3, '-', std::string("hello "), std::string("world"), '!'); util::single_ptr<Test> t; std::printf("%s\n", t->str.c_str()); static const Throat throat("gulp!"); util::single_ptr<IGulp>::init(&throat); util::single_ptr<IGulp> g; std::printf("%s\n", g->how().c_str()); } |
|
#8
|
|
|
|
|
Bronek Kozicki pisze:
> PS2. Jezeli do deklaracji statycznych skladowych dopisac > __declspec(thread), to zrobi sie "single_tls_ptr". Byc moze bardziej > pozyteczny. Bedziemy miec w tym przypadku statyczna zmienna per thread wiec po co nam synchronizacja skoro wszystkie odwolania do danej zmiennej statyczna beda pochodzic z tego samego watku ? |
|
#9
|
|
|
|
|
Uzytkownik Bronek Kozicki napisal:
> template <typename T> > class single_ptr > { > enum {Absent, Coming, Present, Dying}; > static __declspec(align(8)) volatile long ref_counter_; > static __declspec(align(8)) volatile long ref_state_; > static __declspec(align(8)) const T * volatile ref_impl_; Volatile nie jest potrzebne. Interlockedxxxx zapewniaja pelna bariere pamieci. > while (_InterlockedCompareExchange(&ref_state_, Coming, > Absent) != Absent) > ::Sleep(1); Sleep ( 0 ). Podanie 0 do Sleep spowoduje, ze watek zrezygnuje z czasu jaki zostal mu przyznany, ale pozostanie aktywny do wykonania. > const T * operator-> () const > { > return impl_; > } I tutaj najbardziej watpliwy fragment implementacji, jezeli chodzi o sens uzycia tego: 1. Jezeli chcialbym uzyc LF do implementacji singletonu to po to aby, utworzyc go w momencie uzycia: const T * operator-> () const { // inicjacja lock-free. } 2. Moga wystapic wielokrotnie serie tworzenia i niszczenia singletona, zmniejszajac wydajnosc aplikacji: void compute_data ( std::vector<char>& data ) { util::single_ptr<JakisCiezkiObiekt> o; o-> get_data ( data ); // cos tam robimy } // watek 1. for ( int i = 0; i < 10000; ++i ) { compute_data ( ... // watek 2. for ( int i = 10000; i < 20000; ++i ) { compute_data ( ... Jezeli mialbym ochote zastosowania singletonu to zrobilbym to tak: // naglowek: singleton<JakisCiezkiObiekt>& get_instance (); // implementacja namespace { singleton<JakisCiezkiObiekt> object; } singleton<JakisCiezkiObiekt>& get_instance (); Zapewnia mi to utworzenie obiektu przy ladowaniu biblioteki. Jak chce obiekt zainicjowac: // poczatek przetwarzania ( ewentualnie boost::once ). get_instance ().init ( .... // koniec: get_instance ().release (); Z ta uwaga, ze init tworzy nowy obiekt a release go niszczy. Pozdrawiam KK |
|
#10
|
|
|
|
|
Krzysiek Kowaliczek pisze:
> Uzytkownik Bronek Kozicki napisal: > >> template <typename T> >> class single_ptr >> { >> enum {Absent, Coming, Present, Dying}; >> static __declspec(align(8)) volatile long ref_counter_; >> static __declspec(align(8)) volatile long ref_state_; >> static __declspec(align(8)) const T * volatile ref_impl_; > > Volatile nie jest potrzebne. Interlockedxxxx zapewniaja pelna > bariere pamieci. Interlocked wymagaja adresu volatile * long [__int64] i ma to drugie dno w msvc w zakresie zapewnienia kolejnosci zapisow do pamieci. LONG __cdecl InterlockedExchange( __inout LONG volatile *Target, __in LONG Value ); |
|
#11
|
|
|
|
|
Bronek Kozicki wrote:
> 1. odgadnac co ten kod robi i do czego moze byc przydatny Leniwy singleton lock-free, a przynajmniej jego czesc inicjalizacyjna. Bo czesc sprzatajaca zawiera w sobie stara dobra bombe nieokreslonosci kasowania zmiennych statycznych miedzy jednostkami translacji. > 3. zmienic na jedno (albo oba) z > a) (opcjonalne) poprawny Skoro uzywasz wariantu DCLP, to po co ten singleton ma byc lock-free? > b) przenosny Jak klepniecie std::atomic, to bedzie :-) > c) prostszy Tutaj prostota nie jest wskazana. Lepsza jest poprawnosc. Ale jesli mozna sobie na to pozwolic, to ja bym uzyl optymistycznej kontroli wspólbieznosci -- niech kazdy watek w ciemno tworzy swój singleton, a tylko jednemu uda sie podstawienie globalne, reszta poniszczy nadmiarowe kopie. W praktyce czesto taniej i latwiej zrobic cos kilka razy i potem posprzatac, niz wymyslac zlozony protokól zabezpieczajacy. Dokladnie z tego powodu chcialbym miec metody reset_if() oraz transfer_if() w std::unique_ptr, ale w cuda nie wierze, wiec stosowny wskazniczek napisalem sobie sam. Pozdrawiam Piotr Wyderski |
|
#12
|
|
|
|
|
Krzysiek Kowaliczek wrote:
> Sleep ( 0 ). > Podanie 0 do Sleep spowoduje, ze watek zrezygnuje z czasu jaki > zostal mu przyznany, ale pozostanie aktywny do wykonania. Ja wole SwitchToThread(). > 2. Moga wystapic wielokrotnie serie tworzenia i niszczenia singletona, > zmniejszajac wydajnosc aplikacji: Prawda, ale to czesciej bywa zaleta niz wada. Tylko trzeba sobie przestroic punkt widzenia. ;-) Pozdrawiam Piotr Wyderski |
|
#13
|
|
|
|
|
Piotr Wyderski pisze:
> Bronek Kozicki wrote: >> c) prostszy > > Tutaj prostota nie jest wskazana. Lepsza jest poprawnosc. > Ale jesli mozna sobie na to pozwolic, to ja bym uzyl optymistycznej > kontroli wspólbieznosci -- niech kazdy watek w ciemno tworzy > swój singleton, a tylko jednemu uda sie podstawienie globalne, > reszta poniszczy nadmiarowe kopie. W praktyce czesto taniej > i latwiej zrobic cos kilka razy i potem posprzatac, niz wymyslac > zlozony protokól zabezpieczajacy. Dokladnie z tego powodu > chcialbym miec metody reset_if() oraz transfer_if() w std::unique_ptr, > ale w cuda nie wierze, wiec stosowny wskazniczek napisalem sobie sam. Tu to samo pomyslalem ale doszedlem do wniosku ze autorowi moze zalezec na fakcie iz powstaje dokladnie jedna instancja T otwierajaca np ten sam port IP. |
|
#14
|
|
|
|
|
On 29/11/2009 22:11, arturbac wrote:
> Bronek Kozicki pisze: >> PS2. Jezeli do deklaracji statycznych skladowych dopisac >> __declspec(thread), to zrobi sie "single_tls_ptr". Byc moze bardziej >> pozyteczny. > > Bedziemy miec w tym przypadku statyczna zmienna per thread wiec po co > nam synchronizacja skoro wszystkie odwolania do danej zmiennej statyczna > beda pochodzic z tego samego watku ? sluszna uwaga. Oczywiscie "single_tls_ptr" moze byc znacznie prostszy B. |
|
#15
|
|
|
|
|
On 30/11/2009 09:04, Krzysiek Kowaliczek wrote:
> Volatile nie jest potrzebne. Interlockedxxxx zapewniaja pelna > bariere pamieci. naglówki Win32 tego wymagaja i nie mam ochoty sie z nimi klócic. >> while (_InterlockedCompareExchange(&ref_state_, Coming, Absent) != >> Absent) >> ::Sleep(1); > > Sleep ( 0 ). > Podanie 0 do Sleep spowoduje, ze watek zrezygnuje z czasu jaki > zostal mu przyznany, ale pozostanie aktywny do wykonania. a jezeli czekam na watek o niskim priorytecie? Z tego co pamietam, tylko Sleep(1) odda mu scheduler. > 1. Jezeli chcialbym uzyc LF do implementacji singletonu to po to aby, > utworzyc go w momencie uzycia: to jest funkcja konstruktora, a dokladniej licznika referencji. Tworzysz raz (pierwsze zawolanie konstruktora), uzywasz kiedy potrzebujesz (kolejne zawolanie konstruktora), i nawet (woohoo! singletony tego nie maja!) przy rozsadnym projekcie, masz przewidywalna destrukcje. > 2. Moga wystapic wielokrotnie serie tworzenia i niszczenia singletona, > zmniejszajac wydajnosc aplikacji: wlasnie dlatego sugeruje utworzyc go wczesniej. > singleton<JakisCiezkiObiekt>& get_instance (); bleee. jakie paskudne slowo "singleton". > // poczatek przetwarzania ( ewentualnie boost::once ). > get_instance ().init ( .... > > // koniec: > get_instance ().release (); > > Z ta uwaga, ze init tworzy nowy obiekt a release go niszczy. jezeli teraz zastosujesz RAII i opakujesz init() w konstruktor a release w destruktor, to uzyskasz klase podobna do single_ptr . Z tym ze ja dodatkowo rozbilem wolanie konstruktora od przekazywania mu parametrów. B. |
|
|
|
|
| Podobne wątki | |
| biblioteka swt-M20070212-1330-win32-win32-x86 Witam! Mogę prosić o krutką acz kolwiek konkretną isnstrukcję instalacji biblioteki swt w eclipse 3.2.2.Chodzi mi o to co i gdzie wklejić!? Z góry dziękuję!! |
|
| dla chetnych v500 Witam.Dla chetnych posiadaczy motorola v300,V500 i wyzej podaje linka do strony (znalazlem w necie) z dzwonkami w formacie mp3,tapetami itd.Pozdrawiam |
|
| win32 api, win32.hlp, pliki naglowkowe - dziwy witam zapragnalem popisac sobie troche "niestandardowych" rzeczy i pojawily sie problemy: - w pomocy win32.hlp brakuje mi opisu niektorych struktur np. LPNMTVCUSTOMDRAW oraz... |
|
| Biznes dla chetnych Biznes mailingowy dla chetnych i ambitnych ludzi ktorzy chca zarobic troche grosza wiecej informacji na priv :) |
|
|
Czasy w strefie GMT. Teraz jest 22:02. | Privacy Policy
|