国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

C++并發(fā)編程(6):單例模式、once_flag與call_once、call_once實現(xiàn)單例

這篇具有很好參考價值的文章主要介紹了C++并發(fā)編程(6):單例模式、once_flag與call_once、call_once實現(xiàn)單例。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

單例模式

參考博客

【C++】單例模式(餓漢模式、懶漢模式)

C++單例模式總結(jié)與剖析

餓漢單例模式 C++實現(xiàn)

C++單例模式(餓漢式)

設(shè)計模式(Design Pattern)是一套被反復(fù)使用、多數(shù)人知曉的、經(jīng)過分類的、代碼設(shè)計經(jīng)驗的總結(jié)
,一共有23種經(jīng)典設(shè)計模式

使用設(shè)計模式的目的:為了代碼可重用性、讓代碼更容易被他人理解、保證代碼可靠性

設(shè)計模式使代碼編寫真正工程化,設(shè)計模式是軟件工程的基石脈絡(luò),如同大廈的結(jié)構(gòu)一樣

單例模式是設(shè)計模式中最常用的一種模式,一個類只能創(chuàng)建一個對象,即單例模式,該模式可以保證系統(tǒng)中該類只有一個實例,并提供一個訪問它的全局訪問點,該實例被所有程序模塊共享

基礎(chǔ)要點:

  • 全局只有一個實例:static 特性,同時禁止用戶自己聲明并定義實例(把構(gòu)造函數(shù)設(shè)為 private)
  • 線程安全
  • 禁止賦值和拷貝
  • 用戶通過接口獲取實例:使用 static 類成員函數(shù)

單例的實現(xiàn)主要有餓漢式和懶漢式兩種,分別進行介紹

餓漢式

不管你將來用不用,程序啟動時就創(chuàng)建一個唯一的實例對象

優(yōu)點:簡單

缺點:可能會導(dǎo)致進程啟動慢,且如果有多個單例類對象實例啟動順序不確定

示例代碼:

//  Hunger_Singleton_pattern
//  Created by lei on 2022/05/13

#include <iostream>
#include <memory>
using namespace std;

class Example
{
public:
    typedef shared_ptr<Example> Ptr;
    static Ptr GetSingleton()
    {
        cout << "Get Singleton" << endl;
        return single;
    }

    void test()
    {
        cout << "Instance location:" << this << endl;
    }

    ~Example() { cout << "Deconstructor called" << endl; };

private:
    static Ptr single;
    Example() { cout << "Constructor called" << endl; };
    Example &operator=(const Example &examp) = delete;
    Example(const Example &examp) = delete;
};
Example::Ptr Example::single = shared_ptr<Example>(new Example);

int main()
{
    Example::Ptr a = Example::GetSingleton();
    Example::Ptr b = Example::GetSingleton();
    a->test();
    b->test();
    cout << "main end" << endl;
    return 0;
}

打印輸出:

Constructor called
Get Singleton
Get Singleton
Instance location:0x55d43c9dce70
Instance location:0x55d43c9dce70
main end
Deconstructor called

可以看到拷貝構(gòu)造函數(shù)只調(diào)用了一次,并且兩個對象內(nèi)存地址相同,說明該類只能實例化一個對象

餓漢單例模式的靜態(tài)變量的初始化由C++完成,規(guī)避了線程安全問題,所以餓漢單例模式是線程安全的

在大多數(shù)情況下使用餓漢單例模式是沒有問題的

有缺陷的懶漢模式

懶漢式(Lazy-Initialization)的方法是直到使用時才實例化對象,也就說直到調(diào)用get_instance() 方法的時候才 new 一個單例的對象, 如果不被調(diào)用就不會占用內(nèi)存

//  Defect_Lazy_Singleton_pattern
//  Created by lei on 2022/05/13

#include <iostream>
#include <thread>
using namespace std;

class Singleton
{
private:
    Singleton()
    {
        cout << "constructor called!" << endl;
    }
    Singleton(const Singleton &) = delete;
    Singleton& operator=(const Singleton &) = delete;
    static Singleton *m_instance_ptr;

public:
    ~Singleton()
    {
        cout << "destructor called!" << endl;
    }
    static Singleton *get_instance()
    {
        if (m_instance_ptr == nullptr)
        {
            m_instance_ptr = new Singleton;
        }
        return m_instance_ptr;
    }
    void use() const { cout << "in use" << endl; }
};

Singleton *Singleton::m_instance_ptr = nullptr;     //靜態(tài)成員變量類內(nèi)聲明類外初始化

int main()
{
    Singleton *instance = Singleton::get_instance();
    Singleton *instance_2 = Singleton::get_instance();

    // thread t1(Singleton::get_instance);
    // thread t2(Singleton::get_instance);
    // thread t3(Singleton::get_instance);
    // thread t4(Singleton::get_instance);

    // t1.join();
    // t2.join();
    // t3.join();
    // t4.join();

    return 0;
}

打印輸出:

constructor called!

取了兩次類的實例,卻只有一次類的構(gòu)造函數(shù)被調(diào)用,表明只生成了唯一實例,這是個最基礎(chǔ)版本的單例實現(xiàn),存在以下問題

1、當(dāng)多線程獲取單例時有可能引發(fā)競態(tài)條件:第一個線程在if中判斷?m_instance_ptr
是空的,于是開始實例化單例;同時第2個線程也嘗試獲取單例,這個時候判斷m_instance_ptr
還是空的,于是也開始實例化單例;這樣就會實例化出兩個對象

2、類中只負責(zé)new出對象,卻沒有負責(zé)delete對象,因此只有構(gòu)造函數(shù)被調(diào)用,析構(gòu)函數(shù)卻沒有被調(diào)用,因此會導(dǎo)致內(nèi)存泄漏

改進的懶漢模式

對應(yīng)上面兩個問題,有以下解決方法:

1、用mutex加鎖

2、使用智能指針

//  Improve_Lazy_Singleton_pattern
//  Created by lei on 2022/05/13

#include <iostream>
#include <memory> // shared_ptr
#include <mutex>  // mutex
#include <thread>
using namespace std;

class Singleton
{
public:
    typedef shared_ptr<Singleton> Ptr;
    ~Singleton()
    {
        cout << "destructor called!" << endl;
    }
    Singleton(const Singleton &) = delete;
    Singleton& operator=(const Singleton &) = delete;
    static Ptr get_instance()
    {
        // "double checked lock"
        if (m_instance_ptr == nullptr)
        {
            lock_guard<mutex> lk(m_mutex);
            if (m_instance_ptr == nullptr)
            {
                m_instance_ptr = shared_ptr<Singleton>(new Singleton);
            }
        }
        return m_instance_ptr;
    }

private:
    Singleton()
    {
        cout << "constructor called!" << endl;
    }
    static Ptr m_instance_ptr;
    static mutex m_mutex;
};

// initialization static variables out of class
Singleton::Ptr Singleton::m_instance_ptr = nullptr;
mutex Singleton::m_mutex;

int main()
{
    Singleton::Ptr instance = Singleton::get_instance();
    Singleton::Ptr instance2 = Singleton::get_instance();

    // thread t1(Singleton::get_instance);
    // thread t2(Singleton::get_instance);
    // thread t3(Singleton::get_instance);
    // thread t4(Singleton::get_instance);

    // t1.join();
    // t2.join();
    // t3.join();
    // t4.join();

    return 0;
}

打印輸出:

constructor called!
destructor called!

只構(gòu)造了一次實例,并且發(fā)生了析構(gòu)

缺陷是雙檢鎖依然會失效,具體原因可以看下面的文章

https://www.drdobbs.com/cpp/c-and-the-perils-of-double-checked-locki/184405726

推薦的懶漢模式

//  Recommand_Lazy_Singleton_pattern
//  Created by lei on 2022/05/13

#include <iostream>
#include <thread>
using namespace std;

class Singleton
{
public:
    ~Singleton()
    {
        cout << "destructor called!" << endl;
    }
    Singleton(const Singleton &) = delete;
    Singleton &operator=(const Singleton &) = delete;
    static Singleton &get_instance()
    {
        static Singleton instance;
        return instance;
    }

private:
    Singleton()
    {
        cout << "constructor called!" << endl;
    }
};

int main()
{
    Singleton &instance_1 = Singleton::get_instance();
    Singleton &instance_2 = Singleton::get_instance();

    // thread t1(Singleton::get_instance);
    // thread t2(Singleton::get_instance);
    // thread t3(Singleton::get_instance);
    // thread t4(Singleton::get_instance);

    // t1.join();
    // t2.join();
    // t3.join();
    // t4.join();

    return 0;
}

打印輸出:

constructor called!
destructor called!

這種方法又叫做 Meyers’ Singleton Meyer’s的單例, 是著名的寫出《Effective C++》系列書籍的作者 Meyers 提出的。所用到的特性是在C++11標(biāo)準(zhǔn)中的Magic Static特性:

If control enters the declaration concurrently while the variable is being initialized, the concurrent execution shall wait for completion of the initialization
如果當(dāng)變量在初始化的時候,并發(fā)同時進入聲明語句,并發(fā)線程將會阻塞等待初始化結(jié)束

這是最推薦的一種單例實現(xiàn)方式:

  • 通過局部靜態(tài)變量的特性保證了線程安全
  • 不需要使用共享指針,代碼簡潔
  • 注意在使用的時候需要聲明單例的引用?Single&?才能獲取對象

once_flag與call_once

參考博客

C++11于once flag,call_once:分析的實現(xiàn)

C++11實現(xiàn)線程安全的單例模式(使用std::call_once)

在多線程編程中,有一個常見的情景是某個任務(wù)僅僅須要運行一次

在C++11中提供了非常方便的輔助類once_flag與call_once

once_flag和call_once的聲明:

struct once_flag
{
    constexpr once_flag() noexcept;
    once_flag(const once_flag&) = delete;
    once_flag& operator=(const once_flag&) = delete;
};
template<class Callable, class ...Args>
  void call_once(once_flag& flag, Callable&& func, Args&&... args);

}  // std

簡單示例:

//  once_flag and call_once simple example
//  Created by lei on 2022/05/13

#include <iostream>
using namespace std;

once_flag flag;

void do_once()
{
    call_once(flag, [&]()
              { cout << "Called once" << endl; });
}

int main()
{
    std::thread t1(do_once);
    std::thread t2(do_once);
    std::thread t3(do_once);
    std::thread t4(do_once);

    t1.join();
    t2.join();
    t3.join();
    t4.join();
}

打印輸出:

Called once

可以看到4個線程只執(zhí)行了一次do_once( )函數(shù)

call_once實現(xiàn)單例模式

//  Call_once_Singleton_pattern
//  Created by lei on 2022/05/13

#include <iostream>
#include <thread>
#include <mutex>
using namespace std;

once_flag cons_flag;

class A
{
public:
    typedef shared_ptr<A> Ptr;

    void m_print() { cout << "m_a++ = " << ++m_a << endl; }

    static Ptr getInstance(int a)
    {
        cout << "Get instance" << endl;
        if (m_instance_ptr == nullptr)
        {
            lock_guard<mutex> m_lock(m_mutex);
            if (m_instance_ptr == nullptr)
            {
                call_once(cons_flag, [&]()
                          { m_instance_ptr.reset(new A(a)); });
            }
        }
        return m_instance_ptr;
    }

    ~A()
    {
        cout << "Deconstructor called" << endl;
    }

private:
    static mutex m_mutex;
    int m_a;
    static Ptr m_instance_ptr;

    A(int a_) : m_a(a_)
    {
        cout << "Constructor called" << endl
             << "m_a = " << m_a << endl;
    }

    A &operator=(const A &A_) = delete;

    A(const A &A_) = delete;
};
A::Ptr A::m_instance_ptr = nullptr;
mutex A::m_mutex;

void test(int aa)
{
    cout << "Go in test..." << endl;
    A::Ptr tp = A::getInstance(aa);
    cout << "tp location:" << tp << endl;
    tp->m_print();
    cout << endl;
}

int main()
{
    thread t1(test, 1);
    thread t2(test, 2);
    thread t3(test, 3);
    thread t4(test, 4);

    t1.join();
    t2.join();
    t3.join();
    t4.join();

    cout << "main end..." << endl;

    return 0;
}

打印輸出:

Go in test...
Get instance
Constructor called
m_a = 4
tp location:0x7fd964000f30
m_a++ = 5

Go in test...
Get instance
tp location:0x7fd964000f30
m_a++ = 6

Go in test...
Get instance
tp location:0x7fd964000f30
m_a++ = 7

Go in test...
Get instance
tp location:0x7fd964000f30
m_a++ = 8

main end...
Deconstructor called

看到構(gòu)造函數(shù)只調(diào)用了一次,并且類A實例化對象的地址始終相同

上面的兩個示例程序中都用到了lambda表達式,call_once通常結(jié)合lambda一起使用文章來源地址http://www.zghlxwxcb.cn/news/detail-571885.html

到了這里,關(guān)于C++并發(fā)編程(6):單例模式、once_flag與call_once、call_once實現(xiàn)單例的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費用

相關(guān)文章

  • 單例模式的八種寫法、單例和并發(fā)的關(guān)系

    單例模式的八種寫法、單例和并發(fā)的關(guān)系

    為什么需要單例? 節(jié)省內(nèi)存和計算 保證結(jié)果正確 方便管理 無狀態(tài)的工具類:比如日志工具類,不管是在哪里使用,我們需要的只是它幫我們記錄日志信息,除此之外,并不需要在它的實例對象上存儲任何狀態(tài),這時候我們就只需要一個實例對象即可。 全局信息類:比如我

    2024年01月18日
    瀏覽(27)
  • 【設(shè)計模式】詳解單例設(shè)計模式(包含并發(fā)、JVM)

    【設(shè)計模式】詳解單例設(shè)計模式(包含并發(fā)、JVM)

    在軟件開發(fā)中,經(jīng)常需要某些類 只能有唯一的實例 ,比如數(shù)據(jù)庫連接。如何才能保證整個應(yīng)用中只有一個唯一實例?如果靠人為制定的協(xié)定來約束,顯然不能很好的保證這一點。如果要從 語法上約束 ,在面向?qū)ο罄锩妫裁吹胤侥軌蚣s束實例的創(chuàng)建? 顯然,只有 構(gòu)造函數(shù)

    2024年02月15日
    瀏覽(31)
  • 【并發(fā)專題】單例模式的線程安全(進階理解篇)

    【并發(fā)專題】單例模式的線程安全(進階理解篇)

    最近學(xué)習(xí)了JVM之后,總感覺知識掌握不夠深,所以想通過分析經(jīng)典的【懶漢式單例】來加深一下理解。(主要是【靜態(tài)內(nèi)部類】實現(xiàn)單例的方式)。 如果小白想理解單例的話,也能看我這篇文章。我也通過了【前置知識】跟【普通懶漢式】、【雙檢鎖懶漢】、【靜態(tài)內(nèi)部類】

    2024年02月14日
    瀏覽(20)
  • 【Java|多線程與高并發(fā)】設(shè)計模式-單例模式(餓漢式,懶漢式和靜態(tài)內(nèi)部類)

    【Java|多線程與高并發(fā)】設(shè)計模式-單例模式(餓漢式,懶漢式和靜態(tài)內(nèi)部類)

    設(shè)計模式是一種在軟件開發(fā)中常用的解決復(fù)雜問題的方法論。它提供了一套經(jīng)過驗證的解決方案,用于解決特定類型問題的設(shè)計和實現(xiàn)。設(shè)計模式可以幫助開發(fā)人員提高代碼的可重用性、可維護性和可擴展性。 設(shè)計模式有很多,本文主要介紹單例模式. 單例模式是一種創(chuàng)建型設(shè)

    2024年02月11日
    瀏覽(28)
  • 《游戲編程模式》學(xué)習(xí)筆記(六)單例模式 Singleton Pattern

    保證一個類只有一個實例,并且提供了訪問該實例的全局訪問點。 定義這種東西一般都是不說人話的,要想要理解這句話的意思,我們得把它揉開了才能搞明白。 我們先看前半句 “保證一個類只有一個實例”,單例一般使用類來實現(xiàn),也就是說,這個單例類,其有且只能有

    2024年02月12日
    瀏覽(24)
  • 【C++】設(shè)計模式-單例模式

    【C++】設(shè)計模式-單例模式

    目錄 一、單例模式 單例模式的三個要點 針對上述三要點的解決方案 常用的兩類單例模式 ?二、懶漢模式實現(xiàn) 1.基本實現(xiàn) 2.鎖+靜態(tài)成員析構(gòu)單例 3.雙層檢查鎖定優(yōu)化 4.雙層檢查鎖定+智能指針 三、餓漢模式實現(xiàn) 1.基礎(chǔ)實現(xiàn) 2.嵌套內(nèi)部類解決內(nèi)存泄漏 3.智能指針解決內(nèi)存泄漏

    2024年02月16日
    瀏覽(15)
  • C++設(shè)計模式代碼--單例模式

    參考:5. 單例模式(Singleton) (yuque.com) 1、什么是單例模式 保證一個類只有一個實例,并提供一個訪問該實例的全局節(jié)點; 2、什么情況下需要單例模式 某個類的對象在軟件運行之初就創(chuàng)建,并且在軟件的很多地方都需要讀寫這個類的信息;使用單例模式的話,類對象就只要

    2024年02月03日
    瀏覽(31)
  • C++設(shè)計模式:單例模式(十)

    C++設(shè)計模式:單例模式(十)

    1、單例設(shè)計模式 單例設(shè)計模式,使用的頻率比較高,整個項目中某個特殊的類對象只能創(chuàng)建一個 并且該類只對外暴露一個public方法用來獲得這個對象。 單例設(shè)計模式又分懶漢式和餓漢式,同時對于懶漢式在多線程并發(fā)的情況下存在線程安全問題 餓漢式:類加載的準(zhǔn)備階段

    2024年04月14日
    瀏覽(33)
  • 【設(shè)計模式】C++單例模式詳解

    ?個類僅有?個實例,并提供一個訪問它的全局訪問點,該實例被所有程序模塊共享。 那么,我們必須保證: 該類不能被復(fù)制;也不能被公開的創(chuàng)造。 對于 C++ 來說,它的構(gòu)造函數(shù),拷貝構(gòu)造函數(shù)和賦值函數(shù)都不能被公開調(diào)用。 單例模式又分為 懶漢模式 和 餓漢模式 ,它們

    2024年02月05日
    瀏覽(23)
  • C++之單例模式

    單例模式(Singleton Pattern)是 面向?qū)ο笾凶詈唵蔚脑O(shè)計模式之一。這種類型的設(shè)計模式屬于創(chuàng)建型模式,它提供了一種創(chuàng)建對象的最佳方式。 這種模式涉及到一個單一的類,該類負責(zé)創(chuàng)建自己的對象,同時確保只有單個對象被創(chuàng)建。這個類提供了一種訪問其唯一的對象的方式

    2024年01月23日
    瀏覽(33)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包