美文网首页
多线程学习(六)

多线程学习(六)

作者: lxr_ | 来源:发表于2021-11-15 18:55 被阅读0次

一:设计模式

“设计模式”:代码的一些写法(这些写法跟常规写法不怎么一样):程序灵活,维护方便,但是别人接管、阅读代码很痛苦
用“设计模式”理念写出来的代码很晦涩
老外把项目的开发经验、模块划分经验,总结整理成 设计模式(先有开发需求,后有理论总结和整理)
国内不太一样,拿着一个程序(项目)往设计模式上套;一个小小的项目,非要弄几个设计模式进去,本末倒置
设计模式有它独特的有点,要活学活用,不要深陷其中,生搬硬套

二.单例设计模式

单例设计模式使用的频率比较高;
单例:整个项目中,有某个或某些特殊的类只能创建一个对象
单例模式演示(使用delete小技巧):

#include "stdafx.h"

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

//单例类定义:
class MySingleton
{
private:
    MySingleton() {}  //私有化构造函数

private:
    static MySingleton* m_instance; //静态成员函数

public:
    static MySingleton* GetInstance()
    {
        if (m_instance == NULL)
        {
            m_instance = new MySingleton();
            static DeleteSingleton DS;
        }
        return m_instance;
    }

    //delete技巧
    class DeleteSingleton //类中套类,用来释放MySingleton
    {
    public:
        ~DeleteSingleton()//析构函数
        {
            if (MySingleton::m_instance)
            {
                delete MySingleton::m_instance;
                MySingleton::m_instance = NULL;
            }
        }
    };

    void func()
    {
        cout << "测试" << endl;
    }
};

//类外静态变量初始化
MySingleton* MySingleton::m_instance = NULL;

int main()
{
    MySingleton* p_s = MySingleton::GetInstance();  
    //创建一个对象,返回指向该类(MySingleton)对象的指针

    //MySingleton* p_s1 = MySingleton::GetInstance();  //其实p_s1与p_s是指向同一个对象的指针

    //MySingleton p_s2;                             
    //报错,构造函数权限为private,不能创建对象,只能通过GetInstance()创建对象

    p_s->func();                                    
    MySingleton::GetInstance()->func();             //静态成员函数不需要实例化对象也能访问

    return 0;
}

三.单例模式共享数据问题分析、解决

尽可能在创建线程之前在主线程中实例化单例类,并完成数据装载等,确保在多线程中数据为只读,这样就不需要互斥量优化多线程
但是面临的问题是:有时候需要在我们自己创建的线程(而不是主线程)中来创建单例类的对象,这种线程不止一个
我们可能会面临GetInstance()这种成员函数要互斥,因为可能多个线程同时创建单例类对象
下面的线程入口函数中需要创建单例类对象可以使用互斥量来解决此类问题

std::mutex resource_mutex;    //定义互斥锁,防止创建多个单例类对象
//单例类定义:
class MySingleton
{
private:
    MySingleton() {}  //私有化构造函数

private:
    static MySingleton* m_instance; //静态成员函数

public:
    static MySingleton* GetInstance()
    {
        std::unique_lock<std::mutex> myMutex(resource_mutex);//自动加锁(lock())
        if (m_instance == NULL)
        {                                   
            //不加锁的情况下如果一个线程执行到这里,切换到另一个线程执行,则两个线程都会创建对象
            m_instance = new MySingleton();
            static DeleteSingleton DS;
        }
        return m_instance;
    }

    //delete技巧
    class DeleteSingleton //类中套类,用来释放MySingleton
    {
    public:
        ~DeleteSingleton()//析构函数
        {
            if (MySingleton::m_instance)
            {
                delete MySingleton::m_instance;
                MySingleton::m_instance = NULL;
            }
        }
    };

    void func()
    {
        cout << "测试" << endl;
    }
};

//类外静态变量初始化
MySingleton* MySingleton::m_instance = NULL;

//线程入口函数
void mythread()
{
    cout << "我的线程开始执行..." << endl;
    MySingleton* p_s = MySingleton::GetInstance();  //有问题
    cout << "我的线程执行完毕..." << endl;
}
int main()
{
    std::thread myThread1(mythread);
    std::thread myThread2(mythread);
    //这两个线程是同一个入口函数,所以这里会有两个流程同时开始执行mythread函数

    myThread1.join();
    myThread2.join();

    return 0;
}

上面的程序有个问题,如果该函数GetInstance()调用次数较多,则每次调用该函数都要加锁,但其实只要我们创建了一个单例类的对象后,以后的操作就为,并不需要互斥量来保护,所以该方法是低效的。
解决:采用双重检查的方法,代码如下(修改了GetInstance()函数的实现,main函数不变),即在外层多加一次判断,只有第一次条件成立才会进入并加锁,后续调用GetInstance()函数时,不会加锁,提高效率

std::mutex resource_mutex;    //定义互斥锁,防止创建多个单例类对象
//单例类定义:
class MySingleton
{
private:
    MySingleton() {}  //私有化构造函数

private:
    static MySingleton* m_instance; //静态成员函数

public:
    static MySingleton* GetInstance()
    {
        
        //a)if(m_instance!=NULL)条件成立,则m_instance已经被new过了
        //b)if(m_instance==NULL)条件成立,不代表m_instance一定没被new过,
        //也就是不加锁的情况下可能发生两个线程同时创建单例类的对象

        if (m_instance)     //双重锁定(双重检查),可以提高效率(只有第一次条件成立才会进入并加锁)
        {
            std::unique_lock<std::mutex> myMutex(resource_mutex);//自动加锁(lock())
            if (m_instance == NULL)
            {
                //不加锁的情况下如果一个线程执行到这里,切换到另一个线程执行,则两个线程都会创建对象
                m_instance = new MySingleton();
                static DeleteSingleton DS;
            }
        }
        
        return m_instance;
    }

    //delete技巧
    class DeleteSingleton //类中套类,用来释放MySingleton
    {
    public:
        ~DeleteSingleton()//析构函数
        {
            if (MySingleton::m_instance)
            {
                delete MySingleton::m_instance;
                MySingleton::m_instance = NULL;
            }
        }
    };

    void func()
    {
        cout << "测试" << endl;
    }
};

//类外静态变量初始化
MySingleton* MySingleton::m_instance = NULL;

//线程入口函数
void mythread()
{
    cout << "我的线程开始执行..." << endl;
    MySingleton* p_s = MySingleton::GetInstance();  //有问题
    cout << "我的线程执行完毕..." << endl;
}

四.std::call_once(),c++11引入的函数

函数模板第二个参数是一个函数名Function
功能保证函数Function()只被调用一次
call_once具备互斥量能力,效率上比互斥量消耗的资源更少
call_once()需要与一个标记结合使用,这个标记std::once_flag是一个结构;
call_once(通过这个标记来决定对应的函数Function()是否执行,调用call_once()成功后,call_once()就把这个标记设置为一种已调用状态
后续再次调用call_once(),只要once_flag被设置为了“已调用”状态,那么对应的函数Function()就不会再被调用
下面为修改后的单例类程序,添加了一个CreateInstanceOnce()函数。

std::mutex resource_mutex;    //定义互斥锁,防止创建多个单例类对象
std::once_flag myOnceFlag;        //这是个系统定义的标记
//单例类定义:
class MySingleton
{
    static void CreateInstanceOnce()  //只被调用一次
    {
        cout << "CreateInstanceOnce()被执行" << endl;

        m_instance = new MySingleton();
        static DeleteSingleton DS;
    }
private:
    MySingleton() {}  //私有化构造函数

private:
    static MySingleton* m_instance; //静态成员函数

public:
    static MySingleton* GetInstance()
    {
        //假设两个线程同时执行到这里,其中一个线程要等另一个线程执行完CreateInstanceOnce
        std::call_once(myOnceFlag, CreateInstanceOnce);  

        cout << "call_once()执行完毕" << endl;
        
        return m_instance;
    }

    //delete技巧
    class DeleteSingleton //类中套类,用来释放MySingleton
    {
    public:
        ~DeleteSingleton()//析构函数
        {
            if (MySingleton::m_instance)
            {
                delete MySingleton::m_instance;
                MySingleton::m_instance = NULL;
            }
        }
    };

    void func()
    {
        cout << "测试" << endl;
    }
};

//类外静态变量初始化
MySingleton* MySingleton::m_instance = NULL;

//线程入口函数
void mythread()
{
    cout << "我的线程开始执行..." << endl;
    MySingleton* p_s = MySingleton::GetInstance();  //有问题
    cout << "我的线程执行完毕..." << endl;
}

执行结果如下图所示,可以看出CreateInstanceOnce()只被执行了一次。


相关文章

网友评论

      本文标题:多线程学习(六)

      本文链接:https://www.haomeiwen.com/subject/ootqtrtx.html