Linux — 线程池及多线程结尾

这篇具有很好参考价值的文章主要介绍了Linux — 线程池及多线程结尾。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

一、线程池

线程池的应用场景:

线程池示例:

二、线程安全的单例模式

什么是单例模式

什么是设计模式

单例模式的特点

饿汉实现方式和懒汉实现方式

三、STL,智能指针和线程安全

 四、其他常见的各种锁

五、读者写者问题(了解)

读写锁

 读写锁接口


Linux — 线程池及多线程结尾

 

一、线程池

线程池一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利用,还能防止过分调度。可用线程数量应该取决于可用的并发处理器、处理器内核、内存、网络sockets等的数量。

一句话就是我们事先创建好一批线程,有任务到来时就唤醒线程执行任务,没有任务时就休眠。

线程池的应用场景:

  1. 需要大量的线程来完成任务,且完成任务的时间比较短。 WEB服务器完成网页请求这样的任务,使用线程池技术是非常合适的。因为单个任务小,而任务数量巨大,你可以想象一个热门网站的点击次数。 但对于长时间的任务,比如一个Telnet连接请求,线程池的优点就不明显了。因为Telnet会话时间比线程的创建时间大多了。
  2. 对性能要求苛刻的应用,比如要求服务器迅速响应客户请求。
  3. 接受突发性的大量请求,但不至于使服务器因此产生大量线程的应用。突发性大量客户请求,在没有线程池情况下,将产生大量线程,虽然理论上大部分操作系统线程数目最大值不是问题,短时间内产生大量线程可能使内存到达极限,出现错误。

线程池示例:

* 1. 创建固定数量线程组成线程池,循环从任务队列中获取任务对象
* 2. 获取到任务对象后,执行任务对象中的任务接口
 

//pthreadPool.hpp
#pragma once 

#include "thread.hpp"
#include "lockGroud.hpp"
#include "Task.hpp"

#include <vector>
#include <queue>
#include <unistd.h>

using namespace ThreadNS;
static const int gnum = 5;

template<class T>
class ThreadPool;
template<class T>
class ThreadData    //大号结构体,方便我们传入线程的名字,后续再想传入一些内容可以在此添加
{
public:
    ThreadData(ThreadPool<T>* in,const string& name)
        :threadpool(in),_name(name)
        {}
public:
    ThreadPool<T>* threadpool;
    string _name;
};

template<class T>
class ThreadPool
{
public:
    ThreadPool(const int& num = gnum)
        :_num(num)
    {
        pthread_mutex_init(&_mutex,nullptr);    //初始化锁
        pthread_cond_init(&_cond,nullptr);      //初始化条件变量
        for(int i = 0;i<_num;++i)
        {
            _threads.push_back(new Thread());  //创建线程 
        }

    }
    void push(const T& in)
    {
        // pthread_mutex_lock(&_mutex); 
        lockGroud lockgroud(&_mutex);   //RAII的锁
        _Task_queue.push(in);           //给任务队列里插入任务
        pthread_cond_signal(&_cond);    //唤醒一个线程
        // pthread_mutex_unlock(&_mutex);
    }
    void run()
    {
        for(auto& t:_threads)
        {
            //将要传入执行方法的参数打包成结构体传入
            ThreadData<T>* td = new ThreadData<T>(this,t->threadname());   

            cout << t->threadname() << "  准备就绪" << endl;
            t->start(handlerTask,td);   //执行方法
        }
    }
    ~ThreadPool()
    {
        pthread_mutex_destroy(&_mutex);     //销毁锁
        pthread_cond_destroy(&_cond);       //销毁条件变量
        for(auto& t:_threads) delete t;     //销毁线程
    }
    T pop()
    {
        T t = _Task_queue.front();      //取任务
        _Task_queue.pop();              
        return t;
    }
    //一批接口
    pthread_mutex_t* Mutex(){ return &_mutex;}
    void threadlock() { pthread_mutex_lock(&_mutex); }
    void threadunlock() { pthread_mutex_unlock(&_mutex); }
    bool threadempty() { return _Task_queue.empty();}
    void threadwait() { pthread_cond_wait(&_cond,&_mutex);}
private:
    static void* handlerTask(void* args)
    {
        ThreadData<T>* td = static_cast<ThreadData<T>*>(args);
        // ThreadPool<T>* threadpool = static_cast<ThreadPool<T>*>(args);

        while(true)
        {
            T t;
            {
                // td->threadpool->threadlock();
                lockGroud lockgroud(td->threadpool->Mutex()); //RAII
                while(td->threadpool->threadempty())    //任务队列为空
                {
                    td->threadpool->threadwait();       //线程就挂起等待
                }
                t = td->threadpool->pop();              //获取任务
                // td->threadpool->threadunlock();
            }
            
            cout << td->_name << "获取任务,计算结果:"<< t() << endl;            //执行任务
            // sleep(1);
        }
        return nullptr;
    }
private:
    int _num;                   //线程数量
    vector<Thread*> _threads;   //线程池
    queue<T> _Task_queue;       //任务队列
    pthread_mutex_t _mutex;     //互斥锁
    pthread_cond_t _cond;       //条件变量
};
//main.cc
#include "ThreadPool.hpp"
#include <memory>
#include <unistd.h>


int main()
{
    unique_ptr<ThreadPool<Task>> tp(new ThreadPool<Task>());
    tp->run();      //线程先跑起来,等待任务的到来

    int x,y;
    char op;
    while(true)
    {
        cout<< "请输入你要计算的内容"<< endl;
        cout<< "格式如下:x+y" << endl<<">:";
        cin >> x >> op >> y;
        Task t(x,y,op,mymath);  //  创建任务
        tp->push(t);            //  插入任务

        sleep(1);
        cout<< "---------------------------------------"<<endl;
    }
    return 0;
}

// 监控脚本指令->循环查看存在的线程
//while :; do ps -aL | grep Threadpool; sleep 1;echo "##################"; done

 上述代码只是一部分,线程封装的接口,任务的创建,以及锁的接口之前也都有写过,具体完整的代码自己查看吧:

lesson11/7_线程池 · 晚风不及你的笑/MyCodeStorehouse - 码云 - 开源中国 (gitee.com)

Linux — 线程池及多线程结尾

二、线程安全的单例模式

什么是单例模式

单例模式是一种 "经典的, 常用的, 常考的" 设计模式。

什么是设计模式

IT行业这么火, 涌入的人很多. 俗话说林子大了啥鸟都有. 大佬和菜鸡们两极分化的越来越严重. 为了让菜鸡们不太拖大佬的后腿, 于是大佬们针对一些经典的常见的场景, 给定了一些对应的解决方案, 这个就是 设计模式。

单例模式的特点

某些类, 只应该具有一个对象(实例), 就称之为单例。
在很多服务器开发场景中, 经常需要让服务器加载很多的数据 (上百G) 到内存中。此时往往要用一个单例的类来管理这些数据。
 

饿汉实现方式和懒汉实现方式

这个在之前的文章里有说过,感兴趣的可以去看一看:C++进阶 — 特殊类设计_晚风不及你的笑427的博客-CSDN博客

在这里我们对线程池的代码做一点小小的修改让其变成懒汉模式:构造私有,禁止拷贝构造和赋值重载实现,

//PthreadPool.hpp
#pragma once 

#include "thread.hpp"
#include "lockGroud.hpp"
#include "Task.hpp"

#include <vector>
#include <queue>
#include <mutex>
#include <unistd.h>

using namespace ThreadNS;
static const int gnum = 5;

template<class T>
class ThreadPool;
template<class T>
class ThreadData    //大号结构体,方便我们传入线程的名字,后续再想传入一些内容可以在此添加
{
public:
    ThreadData(ThreadPool<T>* in,const string& name)
        :threadpool(in),_name(name)
        {}
public:
    ThreadPool<T>* threadpool;
    string _name;
};

template<class T>
class ThreadPool
{
public:

    void push(const T& in)
    {
        // pthread_mutex_lock(&_mutex); 
        lockGroud lockgroud(&_mutex);   //RAII的锁
        _Task_queue.push(in);           //给任务队列里插入任务
        pthread_cond_signal(&_cond);    //唤醒一个线程
        // pthread_mutex_unlock(&_mutex);
    }
    void run()
    {
        for(auto& t:_threads)
        {
            //将要传入执行方法的参数打包成结构体传入
            ThreadData<T>* td = new ThreadData<T>(this,t->threadname());   

            cout << t->threadname() << "  准备就绪" << endl;
            t->start(handlerTask,td);   //执行方法
        }
    }
    ~ThreadPool()
    {
        pthread_mutex_destroy(&_mutex);     //销毁锁
        pthread_cond_destroy(&_cond);       //销毁条件变量
        for(auto& t:_threads) delete t;     //销毁线程
    }
    T pop()
    {
        T t = _Task_queue.front();      //取任务
        _Task_queue.pop();              
        return t;
    }
    static ThreadPool<T>* GetInstance() //返回对象
    {
        if(nullptr == _tp)//双重判断,降低锁冲突的概率, 提高性能
        {
            _mtx.lock();    //使用互斥锁, 保证多线程情况下也只调用一次 new.
            if(nullptr == _tp)
            {
                _tp = new ThreadPool<T>();
            }
            _mtx.unlock();
        }
        
        return _tp;
    }
    //一批接口
    pthread_mutex_t* Mutex(){ return &_mutex;}
    void threadlock() { pthread_mutex_lock(&_mutex); }
    void threadunlock() { pthread_mutex_unlock(&_mutex); }
    bool threadempty() { return _Task_queue.empty();}
    void threadwait() { pthread_cond_wait(&_cond,&_mutex);}
private:
    ThreadPool(const int& num = gnum)
        :_num(num)
    {
        pthread_mutex_init(&_mutex,nullptr);    //初始化锁
        pthread_cond_init(&_cond,nullptr);      //初始化条件变量
        for(int i = 0;i<_num;++i)
        {
            _threads.push_back(new Thread());  //创建线程 
        }

    }
    ThreadPool(const T&) = delete;
    void operator=(const T&) = delete;
    
    static void* handlerTask(void* args)
    {
        ThreadData<T>* td = static_cast<ThreadData<T>*>(args);
        // ThreadPool<T>* threadpool = static_cast<ThreadPool<T>*>(args);

        while(true)
        {
            T t;
            {
                // td->threadpool->threadlock();
                lockGroud lockgroud(td->threadpool->Mutex()); //RAII
                while(td->threadpool->threadempty())    //任务队列为空
                {
                    td->threadpool->threadwait();       //线程就挂起等待
                }
                t = td->threadpool->pop();              //获取任务
                // td->threadpool->threadunlock();
            }
            
            cout << td->_name << "获取任务,计算结果:"<< t() << endl;            //执行任务
            // sleep(1);
        }
        return nullptr;
    }
private:
    int _num;                   //线程数量
    vector<Thread*> _threads;   //线程池
    queue<T> _Task_queue;       //任务队列
    pthread_mutex_t _mutex;     //互斥锁
    pthread_cond_t _cond;       //条件变量
    volatile static ThreadPool<T>* _tp;  
    static mutex _mtx;
};
template<class T>
ThreadPool<T>* ThreadPool<T>::_tp = nullptr;
template<class T>
mutex ThreadPool<T>::_mtx;
//main.cc
#include "ThreadPool.hpp"
#include <memory>
#include <unistd.h>


int main()
{
    // unique_ptr<ThreadPool<Task>> tp(new ThreadPool<Task>());
    // tp->run();      //线程先跑起来,等待任务的到来

    ThreadPool<Task>::GetInstance()->run();

    int x,y;
    char op;
    while(true)
    {
        cout<< "请输入你要计算的内容"<< endl;
        cout<< "格式如下:x+y" << endl<<">:";
        cin >> x >> op >> y;
        Task t(x,y,op,mymath);  //  创建任务
        // tp->push(t);            //  插入任务
        ThreadPool<Task>::GetInstance()->push(t); 
        sleep(1);
        cout<< "---------------------------------------"<<endl;
    }
    return 0;
}

// 监控脚本指令->循环查看存在的线程
//while :; do ps -aL | grep Threadpool; sleep 1;echo "##################"; done

Linux — 线程池及多线程结尾

 完整代码:lesson11/8_单例线程池 · 晚风不及你的笑/MyCodeStorehouse - 码云 - 开源中国 (gitee.com)


三、STL,智能指针和线程安全

STL中的容器是否是线程安全的?
        不是。原因是, STL 的设计初衷是将性能挖掘到极致, 而一旦涉及到加锁保证线程安全, 会对性能造成巨大的影响.而且对于不同的容器, 加锁方式的不同, 性能可能也不同(例如hash表的锁表和锁桶)。因此 STL 默认不是线程安全。 如果需要在多线程环境下使用, 往往需要调用者自行保证线程安全。

智能指针是否是线程安全的?
        对于 unique_ptr, 由于只是在当前代码块范围内生效, 因此不涉及线程安全问题。
        对于 shared_ptr, 多个对象需要共用一个引用计数变量, 所以会存在线程安全问题. 但是标准库实现的时候考虑到了这个问题, 基于原子操作(CAS)的方式保证 shared_ptr 能够高效, 原子的操作引用计数.


 四、其他常见的各种锁

  • 悲观锁:在每次取数据时,总是担心数据会被其他线程修改,所以会在取数据前先加锁(读锁,写锁,行锁等),当其他线程想要访问数据时,被阻塞挂起。之前用的互斥锁就属于悲观锁
  • 乐观锁:每次取数据时候,总是乐观的认为数据不会被其他线程修改,因此不上锁。但是在更新数据前,会判断其他数据在更新前有没有对数据进行修改。主要采用两种方式:版本号机制和CAS操作。
  • CAS操作:当需要更新数据时,判断当前内存值和之前取得的值是否相等。如果相等则用新值更新。若不等则失败,失败则重试,一般是一个自旋的过程,即不断重试。
  • 自旋锁:轮询式的去申请锁,直至申请成功。

这些锁的等待方式是不同的,是什么决定了最终的等待方式呢?是线程要等待的时长决定的。比如说一个线程申请到锁,访问临界资源的时间很长,那么就需要让其他线程挂起等待,如果访问时间很短,其他线程就可以用自旋的方式去等。如果不知道时间长短怎么决定等待方式呢?可以两种方式都用,分别测试一下,看哪个效率更高用哪个。


五、读者写者问题(了解)

读写锁

在编写多线程的时候,有一种情况是十分常见的。那就是,有些公共数据修改的机会比较少。相比较改写,它们读的机会反而高的多。通常而言,在读的过程中,往往伴随着查找的操作,中间耗时很长。给这种代码段加锁,会极大地降低我们程序的效率。那么有没有一种方法,可以专门处理这种多读少写的情况呢? 有,那就是读写锁。        ​​​​​​​注:写独占,读共享,读锁优先级高
Linux — 线程池及多线程结尾

“321原则”

3种关系:写者和写者 -> 互斥        写者和读者 -> 互斥与同步         读者和读者 -> 没有关系

2种角色:读者和写者

1个交易场所:一段缓冲区

读者写者模型和生产消费模型的本质区别就是 消费者会拿走数据,而读者不会。

使用场景:一次发布,很长时间不做修改,大部分时间都是被读取的,比如写作,或者写博客。

 读写锁接口

  • 初始化

int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,const pthread_rwlockattr_t* restrict attr);

  • 销毁

int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);

  • 加锁和解锁

int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);        //读者加锁
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);       //写者加锁
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);       //不论是读者还是写者,直接解锁

  • 设置读写优先

int pthread_rwlockattr_setkind_np(pthread_rwlockattr_t *attr, int pref);
/*
pref 共有 3 种选择
    PTHREAD_RWLOCK_PREFER_READER_NP (默认设置) 读者优先,可能会导致写者饥饿情况
    PTHREAD_RWLOCK_PREFER_WRITER_NP 写者优先,目前有 BUG,导致表现行为和
    PTHREAD_RWLOCK_PREFER_READER_NP 一致
    PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP 写者优先,但写者不能递归加锁
*/文章来源地址https://www.toymoban.com/news/detail-437883.html


#include <vector>
#include <sstream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
#include <pthread.h>

volatile int ticket = 1000;
pthread_rwlock_t rwlock;

void* reader(void* arg)
{
	char* id = (char*)arg;
	while (1) {
		pthread_rwlock_rdlock(&rwlock);
		if (ticket <= 0) {
			pthread_rwlock_unlock(&rwlock);
			break;
		} 
		printf("%s: %d\n", id, ticket);
		pthread_rwlock_unlock(&rwlock);
		usleep(1);
	} 
	return nullptr;
} 
void* writer(void* arg)
{
	char* id = (char*)arg;
	while (1) {
		pthread_rwlock_wrlock(&rwlock);
		if (ticket <= 0) {
			pthread_rwlock_unlock(&rwlock);
			break;
		} 
		printf("%s: %d\n", id, --ticket);
		pthread_rwlock_unlock(&rwlock);
		usleep(1);
	} 
	return nullptr;
} 
struct ThreadAttr
{
	pthread_t tid;
	std::string id;
};
std::string create_reader_id(std::size_t i)
{
	// 利用 ostringstream 进行 string 拼接
	std::ostringstream oss("thread reader ", std::ios_base::ate);
	oss << i;
	return oss.str();
} 
std::string create_writer_id(std::size_t i)
{
	// 利用 ostringstream 进行 string 拼接
	std::ostringstream oss("thread writer ", std::ios_base::ate);
	oss << i;
	return oss.str();
} 
void init_readers(std::vector<ThreadAttr>& vec)
{
	for (std::size_t i = 0; i < vec.size(); ++i) {
		vec[i].id = create_reader_id(i);
		pthread_create(&vec[i].tid, nullptr, reader, (void*)vec[i].id.c_str());
	}
} 
void init_writers(std::vector<ThreadAttr>& vec)
{
	for (std::size_t i = 0; i < vec.size(); ++i) {
		vec[i].id = create_writer_id(i);
		pthread_create(&vec[i].tid, nullptr, writer, (void*)vec[i].id.c_str());
	}
} 
void join_threads(std::vector<ThreadAttr> const& vec)
{
	// 我们按创建的 逆序 来进行线程的回收
	for (std::vector<ThreadAttr>::const_reverse_iterator it = vec.rbegin(); it !=
		vec.rend(); ++it) {
		pthread_t const& tid = it->tid;
		pthread_join(tid, nullptr);
	}
} 
void init_rwlock()
{
#if 0 // 写优先
	pthread_rwlockattr_t attr;
	pthread_rwlockattr_init(&attr);
	pthread_rwlockattr_setkind_np(&attr, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP);
	pthread_rwlock_init(&rwlock, &attr);
	pthread_rwlockattr_destroy(&attr);
#else // 读优先,会造成写饥饿
	pthread_rwlock_init(&rwlock, nullptr);
#endif
} 
int main()
{
	// 测试效果不明显的情况下,可以加大 reader_nr
	// 但也不能太大,超过一定阈值后系统就调度不了主线程了
	const std::size_t reader_nr = 1000;
	const std::size_t writer_nr = 2;
	std::vector<ThreadAttr> readers(reader_nr);
	std::vector<ThreadAttr> writers(writer_nr);
	init_rwlock();
	init_readers(readers);
	init_writers(writers);
	join_threads(writers);
	join_threads(readers);
	pthread_rwlock_destroy(&rwlock);
}
g++ -std=c++11 -Wall -Werror $^ -o $@ -lpthread

到了这里,关于Linux — 线程池及多线程结尾的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • ES6 reduce方法:示例与详解、应用场景

    还是大剑师兰特 :曾是美国某知名大学计算机专业研究生,现为航空航海领域高级前端工程师;CSDN知名博主,GIS领域优质创作者,深耕openlayers、leaflet、mapbox、cesium,canvas,webgl,echarts等技术开发,欢迎加底部微信(gis-dajianshi),一起交流。 No. 内容链接 1 Openlayers 【入门教

    2024年04月13日
    浏览(38)
  • Java 代理模式的基本概念、使用场景、应用示例和实现方法

    代理模式是一种常见的设计模式,在 Java 开发中被广泛应用。它允许我们通过添加一个代理对象来控制对另一个对象的访问,从而提供了一种间接访问实际对象的方法。本文将详细介绍 Java 代理模式的基本概念、使用场景、应用示例和实现方法等相关内容。 代理模式是一种结

    2024年02月05日
    浏览(59)
  • STM32 MPU (F7 H7)常见应用场景配置示例

    == 引用硬汉嵌入式老哥的教程。== 配置参数可看我另一篇文章。 读写Cache都是关闭的,比如FMC外设驱动,扩展IO,LCD,NOR FLASH SRAM,ETH收发描述符空间 特点:保证严格按照程序代码执行 缺点:不支持非对齐访问 配置 FMC 扩展 IO 的 MPU 属性为 Device 或者 Strongly Ordered 以太网收发描

    2024年04月16日
    浏览(32)
  • Jpa与Druid线程池及Spring Boot整合(一): spring-boot-starter-data-jpa 搭建持久层

                          Jpa与Druid线程池及Spring Boot整合(一) Jpa与Druid线程池及Spring Boot整合(二):几个坑 附录官网文档:core.domain-events域事件 docker实战(一):centos7 yum安装docker docker实战(二):基础命令篇 docker实战(三):docker网络模式(超详细) docker实战(四):docker架构原理 docker实战(五

    2024年02月13日
    浏览(54)
  • IO进程线程,文件与目录,实现linux任意目录下ls -la

    注意文件的名字、路径是如何输入的。 函数opendir打开目录,struct dirent,struct stat这些结构体的含义。          readdir()函数是一个用于读取目录内容的系统调用或库函数,在类Unix操作系统中(如Linux)广泛使用。它用于遍历目录,并逐个获取目录中的条目(文件和子目录

    2024年02月10日
    浏览(41)
  • Azure Arc 概要:功能、管理和应用场景详解,AZ900 考点示例

    本文思维导图概述的主要内容: 在2019年微软的Kindle大会上,一个备受关注的宣布是 Azure Arc ,它是一款专为混合云应用程序和基础设施而设计的全新运维工具。 当今,各家企业正积极应对不断复杂化的业务环境。这些环境横跨多个数据中心、多云和边缘部署。每个环境和云

    2024年02月07日
    浏览(48)
  • Linux和windows进程同步与线程同步那些事儿(三): Linux线程同步详解示例

    Linux和windows进程同步与线程同步那些事儿(一) Linux和windows进程同步与线程同步那些事儿(二): windows线程同步详解示例 Linux和windows进程同步与线程同步那些事儿(三): Linux线程同步详解示例 Linux和windows进程同步与线程同步那些事儿(四):windows 下进程同步 Linux和wi

    2024年02月01日
    浏览(39)
  • Jpa与Druid线程池及Spring Boot整合(二): spring-boot-starter-data-jpa 踏坑异常处理方案

                         docker实战(一):centos7 yum安装docker docker实战(二):基础命令篇 docker实战(三):docker网络模式(超详细) docker实战(四):docker架构原理 docker实战(五):docker镜像及仓库配置 docker实战(六):docker 网络及数据卷设置 docker实战(七):docker 性质及版本选择 认知升维: 道、法、

    2024年02月13日
    浏览(61)
  • 【设计模式】builder 创建者设计模式详解(包含电商应用场景及代码示例)

    在常见的设计模式中,“Builder” 通常是指一种设计模式,而不是具体的类或方法。Builder 模式是一种创建型设计模式,其目的是通过提供一个独立的构建器类来构建一个复杂对象。 建造者模式 (Builder Pattern) 是一种创建型设计模式,它的主要目标是为了将一个复杂对象的构

    2024年01月21日
    浏览(49)
  • 生成器设计模式(Builder Design Pattern)[论点:概念、图示、示例、框架中的应用、场景]

            生成器设计模式(Builder Design Pattern)是一种创建型设计模式,用于处理具有多个属性和复杂构造过程的对象。生成器模式通过将对象的构建过程与其表示分离,使得相同的构建过程可以创建不同的表示。这有助于减少构造函数的参数个数,提高代码的可读性和可维

    2023年04月11日
    浏览(44)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包