Windows-da ip bilan xavfsiz singleton naqshini qanday yaratishim mumkin?

Men bu yerda ip-xavfsiz singleton naqshlari haqida o'qidim:

http://en.wikipedia.org/wiki/Singleton_pattern#C.2B.2B_.28using_pthreads.29

Va pastki qismida aytilishicha, yagona xavfsiz yo'l pthread_once dan foydalanishdir - bu Windowsda mavjud emas.

Bu ipni xavfsiz ishga tushirishni kafolatlashning yagona usulimi?

Men ushbu mavzuni SO da o'qidim:

C++ da singletonning xavfsiz dangasa qurilishi

Va Windows-da o'ylaymanki, atom OS darajasidagi almashtirish va taqqoslash funktsiyasiga ishora qiladi:

http://msdn.microsoft.com/en-us/library/ms683568.aspx

Bu men xohlagan narsani qila oladimi?

Tahrirlash: Men dangasa ishga tushirishni xohlayman va sinfning faqat bitta nusxasi bo'lishini xohlayman.

Boshqa saytdagi kimdir nomlar maydoni ichida global foydalanishni aytib o'tdi (va u singlni anti-naqsh deb ta'riflagan) - qanday qilib u "anti-naqsh" bo'lishi mumkin?

Qabul qilingan javob:
Men qabul qildim Joshning javobi Men Visual Studio 2008 dan foydalanayotganim uchun - Eslatma: Kelajakdagi o'quvchilar uchun, agar siz ushbu kompilyatordan (yoki 2005) foydalanmasangiz - Qabul qilingan javobdan foydalanmang !!

Tahrirlash: Kod qaytish bayonotidan tashqari yaxshi ishlaydi - xatoga yo'l qo'ydim: C2440 xatosi: 'qaytish' : 'uchuvchi Singleton *'dan 'Singleton *'ga aylantirib bo'lmaydi. Qaytish qiymatini o'zgaruvchan Singleton * qilib o'zgartirishim kerakmi?

Tahrirlash: Const_cast‹> oʻzgaruvchan kvalifikatsiyani olib tashlaydi. Joshga yana bir bor rahmat.


person Mark Ingram    schedule 02.10.2008    source manba
comment
Har qanday mavzu yaratishdan oldin singeltonni ishga tushiring.   -  person Martin York    schedule 03.10.2008
comment
Singelton anti-naqshdir, chunki u asosan noto'g'ri ishlatilgan. ya'ni global varibale o'rnini bosuvchi sifatida.   -  person Martin York    schedule 03.10.2008
comment
Global o'zgaruvchilar C dan yomon o'tish emasmi?   -  person Mark Ingram    schedule 03.10.2008
comment
Joshning javobida CRITICAL_SECTION "cs" qanday ishga tushirilgan?   -  person Matthew Murdoch    schedule 04.10.2008
comment
Singletonni qanday amalga oshirish bo'yicha ajoyib munozarani C++ da ip xavfsizligi bilan bir qatorda ushbu maqolada topish mumkin: aristeia.com/Papers/DDJ%5FJul%5FAug%5F2004%5Frevised.pdf   -  person    schedule 30.10.2009


Javoblar (9)


Agar siz Visual C++ 2005/2008 dan foydalanayotgan bo'lsangiz, "o'zgaruvchan o'zgaruvchilar o'zini panjara kabi tutadi". Bu dangasa ishga tushirilgan singletonni amalga oshirishning eng samarali usuli.

MSDN jurnalidan:

Singleton* GetSingleton()
{
    volatile static Singleton* pSingleton = 0;

    if (pSingleton == NULL)
    {
        EnterCriticalSection(&cs);

        if (pSingleton == NULL)
        {
            try
            {
                pSingleton = new Singleton();
            }
            catch (...)
            {
                // Something went wrong.
            }
        }

        LeaveCriticalSection(&cs);
    }

    return const_cast<Singleton*>(pSingleton);
}

Singletonga kirish kerak bo'lganda, GetSingleton() ga qo'ng'iroq qiling. Birinchi marta chaqirilganda, statik ko'rsatkich ishga tushiriladi. U ishga tushirilgandan so'ng, NULL tekshiruvi faqat ko'rsatgichni o'qish uchun blokirovka qilishni oldini oladi.

Uni har qanday kompilyatorda ishlatmang, chunki u portativ emas. Standart bu qanday ishlashiga kafolat bermaydi. Visual C++ 2005 buni amalga oshirish uchun o'zgaruvchan semantikaga aniq qo'shiladi.

Siz e'lon qilishingiz va MUHIM bo'limni ishga tushirishingiz kerak kodning boshqa joyida. Ammo bu ishga tushirish arzon, shuning uchun dangasa ishga tushirish odatda muhim emas.

person Eclipse    schedule 02.10.2008
comment
Bu ishlamayapti: ushbu maqolani o'qing erdani.org/publications/DDJ_Jul_Aug_2004_revised.pdf - person Martin York; 03.10.2008
comment
Bu kod istisnolardan xavfsiz emas: agar istisnolar ucha boshlasa, LeaveCriticalSection() chaqirilmaydi. - person Martin York; 03.10.2008
comment
Bundan tashqari, siz taqdim etgan maqola 2004 yil, Josh tomonidan taqdim etilgan maqola esa 2007 yil. - person Mark Ingram; 03.10.2008
comment
Uchib ketadigan yagona istisnolar try/catch bloki tomonidan ushlanadi, lekin siz haqsiz, Enter/Leave CriticalSection-ni o'rash RAII o'rash sinfi tomonidan amalga oshirilishi kerak. - person Eclipse; 03.10.2008
comment
Martin: Visual C++ 2005 siz havola qilgan maqolada ko'tarilgan muammolarni hal qilish uchun o'zgaruvchan semantikani o'zgartirdi. Agar siz buni 6.0 da sinab ko'rsangiz, protsessoringiz ko'rsatmalarni qayta tartiblaganda yoki sizda ko'p yadroli tizim mavjud bo'lganda muammoga duch kelasiz. - person Eclipse; 03.10.2008
comment
Josh to'g'ri. Shuni ta'kidlash kerakki, bu erda "uchuvchi" dan foydalanish Visual C++ ga xos bo'lib, agar kod boshqa Windows kompilyatori (Intel, gcc va boshqalar) bilan tuzilgan bo'lsa yoki boshqa operatsion tizimga o'tkazilgan bo'lsa, ip xavfsizligini ta'minlamaydi. - person Matthew Murdoch; 03.10.2008
comment
Qaytish bayonotidan tashqari kodingiz yaxshi ishlaydi - men xatoga yo'l qo'ydim: C2440 xatosi: "qaytish" : "uchuvchi Singleton " dan "Singleton *" ga aylantirib bo'lmaydi. Qaytish qiymatini o'zgaruvchan Singletonga o'zgartirishim kerakmi? - person Mark Ingram; 03.10.2008
comment
Siz const_cast‹Singleton *›(pSingleton) bilan xavfsiz bo'lasiz, chunki o'zgaruvchan semantika faqat muhim bo'limda kerak. - person Eclipse; 03.10.2008
comment
const_cast‹› o'zgaruvchan kvalifikatsiyani olib tashlaydimi? Aqldan ozgan narsalar. Yaxshi ishladi-da. Rahmat! - person Mark Ingram; 03.10.2008
comment
CRITICAL_SECTION "cs" qanday ishga tushirilmoqda? - person Matthew Murdoch; 04.10.2008
comment
Kichik nit: siz 0 ni tayinlaysiz, lekin NULLga qarshi test qilasiz. Sinovda 0 dan foydalaning yoki !pSingleton dan foydalaning - person camh; 04.10.2008
comment
GetSingleton chaqirilishidan oldin CRITICAL_SECTION ishga tushirilganiga qanday kafolat bera olaman? - person deft_code; 11.11.2009

Singletonningoʻzaro platformali tarmoqli xavfsiz ishga tushirilishini kafolatlashning oddiy yoʻli uni ilovangizning asosiy oqimida toʻgʻridan-toʻgʻri amalga oshirishdir (singldagi statik aʼzo funksiyasiga qoʻngʻiroq qilish orqali). Sizning ilovangiz har qanday boshqa mavzularni (yoki hech bo'lmaganda singletonga kirishga imkon beradigan boshqa mavzularni) ishga tushiradi.

Singletonga ipning xavfsiz kirishini ta'minlash, keyinchalik mutekslar/tanqidiy bo'limlar bilan odatdagi tarzda amalga oshiriladi.

Shunga o'xshash mexanizm yordamida dangasa ishga tushirishga ham erishish mumkin. Bu bilan duch keladigan odatiy muammo shundaki, ip xavfsizligini ta'minlash uchun zarur bo'lgan mutex ko'pincha singletonning o'zida ishga tushiriladi, bu shunchaki ip xavfsizligi muammosini mutex/tanqidiy qismni ishga tushirishga olib keladi. Ushbu muammoni hal qilishning bir usuli - ilovangizning asosiy oqimida mutex/kritik bo'limni yaratish va ishga tushirish, so'ngra uni statik a'zo funksiyasiga qo'ng'iroq qilish orqali singletonga o'tkazish. Singletonning og'ir vaznli ishga tushirilishi ushbu oldindan ishga tushirilgan mutex/kritik bo'limdan foydalangan holda xavfsiz tarzda amalga oshirilishi mumkin. Masalan:

// A critical section guard - create on the stack to provide 
// automatic locking/unlocking even in the face of uncaught exceptions
class Guard {
    private:
        LPCRITICAL_SECTION CriticalSection;

    public:
        Guard(LPCRITICAL_SECTION CS) : CriticalSection(CS) {
            EnterCriticalSection(CriticalSection);
        }

        ~Guard() {
            LeaveCriticalSection(CriticalSection);
        }
};

// A thread-safe singleton
class Singleton {
    private:
        static Singleton* Instance;
        static CRITICAL_SECTION InitLock;
        CRITICIAL_SECTION InstanceLock;

        Singleton() {
            // Time consuming initialization here ...

            InitializeCriticalSection(&InstanceLock);
        }

        ~Singleton() {
            DeleteCriticalSection(&InstanceLock);
        }

    public:
        // Not thread-safe - to be called from the main application thread
        static void Create() {
            InitializeCriticalSection(&InitLock);
            Instance = NULL;
        }

        // Not thread-safe - to be called from the main application thread
        static void Destroy() {
            delete Instance;
            DeleteCriticalSection(&InitLock);
        }

        // Thread-safe lazy initializer
        static Singleton* GetInstance() {
            Guard(&InitLock);

            if (Instance == NULL) {
                Instance = new Singleton;
            }

            return Instance;
        }

        // Thread-safe operation
        void doThreadSafeOperation() {
            Guard(&InstanceLock);

            // Perform thread-safe operation
        }
};

Biroq, singllardan umuman foydalanmaslik uchun yaxshi sabablar bor (va nima uchun ular ba'zananti-naqshdeb ataladi):

  • Ular mohiyatan ulug'langan global o'zgaruvchilardir
  • Ular ilovaning turli qismlari o'rtasida yuqori ulanishga olib kelishi mumkin
  • Ular birlik sinovini murakkablashtirishi yoki imkonsiz qilishi mumkin (haqiqiy singletonlarni soxta ilovalar bilan almashtirish qiyinligi sababli)

Muqobil variant - "mantiqiy singleton" dan foydalanish, uning yordamida siz asosiy oqimda sinfning bitta nusxasini yaratasiz va ishga tushirasiz va uni talab qiladigan ob'ektlarga o'tkazasiz. Singleton sifatida yaratmoqchi bo'lgan ko'plab ob'ektlar mavjud bo'lganda, bu yondashuv noqulay bo'lishi mumkin. Bunday holda, bir-biriga o'xshamaydigan ob'ektlar bitta "Kontekst" ob'ektiga birlashtirilishi mumkin, ular keyinchalik kerak bo'lganda o'tkaziladi.

person Community    schedule 02.10.2008

Qabul qilingan yechim menga yoqsa-da, men boshqa istiqbolli yetakchi topdim va uni shu yerda baham ko'rishim kerak deb o'yladim: Bir martalik ishga tushirish (Windows)

person TripShock    schedule 20.04.2012

Ish zarralarini xavfsiz ishga tushirishni ta'minlash uchun siz mutex yoki kritik bo'lim kabi OT primitividan foydalanishingiz mumkin, ammo bu sizning singleton ko'rsatgichingizga har safar kirishda (qulflanganligi sababli) qo'shimcha xarajatlarga olib keladi. U portativ ham emas.

person Henk    schedule 02.10.2008

Bu savol uchun e'tiborga olishingiz kerak bo'lgan aniq bir nuqta bor. Sizga kerakmi ...

  1. Sinfning bitta va faqat bitta nusxasi aslida yaratilgan
  2. Sinfning ko'p nusxalari yaratilishi mumkin, lekin sinfning faqat bitta haqiqiy aniq nusxasi bo'lishi kerak

Ushbu naqshlarni C++ da amalga oshirish uchun Internetda ko'plab namunalar mavjud. Mana Kod loyihasi namunasi

person JaredPar    schedule 02.10.2008
comment
Birinchisi (faqat bitta sinf yaratilishi mumkin) - person Mark Ingram; 03.10.2008

Quyida buni C# da qanday qilish kerakligi tushuntirilgan, ammo aynan shu kontseptsiya singleton naqshini qo'llab-quvvatlaydigan har qanday dasturlash tiliga tegishli.

http://www.yoda.arachsys.com/csharp/singleton.html

Siz dangasa ishga tushirishni xohlaysizmi yoki yo'qligini hal qilishingiz kerak. Dangasa ishga tushirish, singleton ichidagi ob'ekt birinchi qo'ng'iroqda yaratilganligini anglatadi, masalan:

MySingleton::getInstance()->doWork();

agar bu qo'ng'iroq keyinroq amalga oshirilmasa, maqolada aytib o'tilganidek, iplar o'rtasida poyga holati xavfi mavjud. Biroq, agar siz qo'ysangiz

MySingleton::getInstance()->initSingleton();

Kodingizning eng boshida, agar u ipni xavfsiz deb hisoblasangiz, unda siz endi ishga tushirishda dangasa emassiz, ilovangiz ishga tushganda sizga "biroz" ko'proq ishlov berish quvvati kerak bo'ladi. Biroq, agar shunday qilsangiz, bu poyga sharoitlari bilan bog'liq ko'plab bosh og'riqlarni hal qiladi.

person Eric    schedule 02.10.2008
comment
Menimcha, xuddi shu kontseptsiya hech qanday dasturlash tiliga taalluqli emas, C# uchun bu oson, til statik ctor faqat bir marta chaqirilishini kafolatlaydi va tarmoq uchun xavfsizdir. C++ uchun bu qiyin. - person zhaorufei; 21.06.2012

Agar siz yanada ko'chma va osonroq yechim izlayotgan bo'lsangiz, kuchaytirishga murojaat qilishingiz mumkin.

boost::call_once dan mavzuni xavfsiz saqlash uchun foydalanish mumkin ishga tushirish.

Uni ishlatish juda oddiy va keyingi C++ 0x standartining bir qismi bo'ladi.

person mmocny    schedule 21.10.2008

Savol singlning dangasa tuzilgan yoki yo'qligini talab qilmaydi. Ko'pgina javoblar buni taxmin qilganligi sababli, men birinchi iborani muhokama qilish uchun o'ylayman:

Tilning o'zi ipdan xabardor emasligini va optimallashtirish texnikasini hisobga olsak, portativ ishonchli c++ singleton yozish juda qiyin (agar imkonsiz bo'lsa), "C++ va ikki marta tekshirilgan qulflash xavfi" Skott Meyers va Andrey Aleksandresku tomonidan.

Men CriticalSection-dan foydalanib, Windows platformasidagi ob'ektni sinxronlashtirish uchun javob berishning ko'pchiligini ko'rganman, lekin CriticalSection faqat barcha iplar bitta protsessorda ishlayotganda ip bilan xavfsiz bo'ladi, bugungi kunda bu to'g'ri emas.

MSDN iqtibos keltiradi: "Yagona jarayonning iplari o'zaro istisno sinxronizatsiyasi uchun muhim bo'lim ob'ektidan foydalanishi mumkin. ".

Va http://msdn.microsoft.com/en-us/library/windows/desktop/ms682530%28v=vs.85%29.aspx

batafsilroq aniqlang:

Kritik bo'lim ob'ekti mutex ob'ekti tomonidan taqdim etilganga o'xshash sinxronizatsiyani ta'minlaydi, bundan mustasno, kritik bo'lim faqat bitta jarayonning iplari tomonidan ishlatilishi mumkin.

Endi, agar "dangasa qurilgan" shart bo'lmasa, quyidagi yechim ham o'zaro modul xavfsiz, ham ip bilan xavfsiz va hatto portativdir:

struct X { };

X * get_X_Instance()
{
    static X x;
    return &x;
}
extern int X_singleton_helper = (get_X_instance(), 1);

Bu o'zaro modullar uchun xavfsiz, chunki biz fayl/nom maydoni ko'lamli global ob'ekt o'rniga mahalliy ko'lamli statik ob'ektdan foydalanamiz.

Bu ip uchun xavfsiz, chunki: X_singleton_helper asosiy yoki DllMain ga kirishdan oldin to'g'ri qiymatga tayinlanishi kerak. Bu haqiqat tufayli ham dangasa tuzilgan emas), bu iborada vergul tinish belgisi emas, balki operatordir.

Kompilyator uni optimallashtirishga yo'l qo'ymaslik uchun bu erda "extern" dan aniq foydalaning (Scott Meyers maqolasi haqida tashvishlar, katta dushman - optimallashtiruvchi.), shuningdek, pc-lint kabi statik tahlil qilish vositasini jim turishiga imkon bering. "Main/DllMaindan oldin" - "Effektiv C++ 3rd" 4-bandida "bir torli ishga tushirish qismi" deb nomlangan Skott Meyer.

Biroq, kompilyatorga get_X_instance() qo'ng'irog'ini til standarti bo'yicha optimallashtirishga ruxsat berilganligiga ishonchim komil emas, iltimos izoh bering.

person zhaorufei    schedule 21.06.2012
comment
Men protsessor va jarayonning chalkashligi haqida qanchalik ahmoq ekanligimga qoyil qolishim kerak. Critical, albatta, bir jarayon ichida bo'lsa, ko'p protsessorli/ko'p yadroli apparatda ishlatilishi mumkin. Yuqoridagi noto'g'ri bo'limni olib tashlash o'rniga, men uni shu yerda qoldiraman va o'zimning aybim haqida eslatish uchun ushbu sharhdan foydalanaman. - person zhaorufei; 27.04.2014

Windowsda ipni xavfsiz Singleton* ishga tushirishni amalga oshirishning ko'plab usullari mavjud. Aslida, ularning ba'zilari hatto o'zaro faoliyat platformalardir. Siz bogʻlagan SO ipida ular C tilida dangasalik bilan tuzilgan Singletonni qidirmoqdalar, bu biroz aniqroq va siz ishlayotgan xotira modelining nozik tomonlarini hisobga olgan holda toʻgʻri bajarish biroz qiyinroq boʻlishi mumkin. .

  • siz hech qachon foydalanmasligingiz kerak
person 1800 INFORMATION    schedule 02.10.2008