当前位置: 首页 > news >正文

网络编程(13)——单例模式

十三、day13

今天学习如何单例模式实现逻辑层的设计。内容包括服务器如何能捕获信号使其安全退出、单例模标类

1. 什么是单例模式?

单例模式(Singleton),保证一个类仅有一个实例,并提供一个访问它的全局访问点,单例模式是在内存中仅会创建一次对象的设计模式。

//通过静态成员变量实现单例
//懒汉式
class Single2
{
private:Single2() {}Single2(const Single2 &) = delete;Single2& operator=(const Single2&) = delete;
public:static Single2& GetInst(){static Single2 single;return single;}
};
  • 在上面的代码块中,定义了一个Single2类,Single2类的默认构造函数被声明为私有,且删除拷贝构造函数和赋值运算符,确保Single2类不能通过拷贝创建或赋值创建新的实例。

  • Single2类只有一个公共静态方法GetInst(),用于获取Single2类的唯一实例。

  • 局部静态成员single用于存储Single2类的唯一实例,通过返回single即可返回该实例。

单例模式的简单实现可总结为:

  • 构造方法是私有的

  • 对外暴露的获取访问是公有的静态的

  • 唯一实例的存储方式是静态的

风险:上述代码块(懒汉式)生成了唯一实例,但在多线程方式下生成的实例可能会存在多个(如果多个线程同时调用GetInst()时都会去实例化一个simgle对象,使得Single2类被重复实例化)

1)单例模式的分类

  • 饿汉式:类加载就会导致该单实例对象被创建

  • 懒汉式:类加载不会导致该单实例对象被创建,而是首次使用该对象时被创建

懒汉式创建对象的方法是函数中创建静态局部变量,这样只有在对象第一次被使用时才会创建实例;而饿汉式一般已经在类中提前声明了静态变量single,这样在类加载时便已经提前创建好实例。

上述代码块的单例模式就是通过懒汉式实现的,静态变量single在第一次使用Single2类的GetInst()时被创建,其声明周期随着进程结束而结束。

饿汉式单例模式实现

//饿汉式
class Single2Hungry
{
private:Single2Hungry() { }Single2Hungry(const Single2Hungry&) = delete;Single2Hungry& operator=(const Single2Hungry&) = delete;
public:static Single2Hungry* GetInst(){if (single == nullptr)single = new Single2Hungry();return single;}
private:static Single2Hungry* single;
};

饿汉模式在类加载时已经创建好该对象,在程序调用时直接返回该单例对象即可,可以避免线程安全问题。

多线程和单线程下进行测试

//饿汉式初始化
Single2Hungry* Single2Hungry::single = Single2Hungry::GetInst();
void thread_func_s2(int i)
{cout << "this is thread " << i << endl;cout << "inst is " << Single2Hungry::GetInst() << endl;
}
void test_single2hungry()
{cout << "s1 addr is " << Single2Hungry::GetInst() << endl;cout << "s2 addr is " << Single2Hungry::GetInst() << endl;for (int i = 0; i < 3; i++){thread tid(thread_func_s2, i);tid.join();}
}
int main(){test_single2hungry()
}

输出为

s1 addr is 0x1e4b00
s2 addr is 0x1e4b00
this is thread 0
inst is 0x1e4b00
this is thread 1
inst is 0x1e4b00
this is thread 2
inst is 0x1e4b00

可见无论单线程还是多线程模式下,通过静态成员变量的指针实现的单例类都是唯一的。饿汉式是在程序启动时就进行单例的初始化,这种方式也可以通过懒汉式调用,无论饿汉式还是懒汉式都存在一个问题,就是什么时候释放内存?多线程情况下,释放内存就很难了,还有二次释放内存的风险。

2)懒汉式的改进

上面提到了懒汉式有一定的风险:在多线程下可能会创建多个Single2的实例,如果多个线程同时调用GetInst()时都会去实例化一个simgle对象,使得Single2类被重复实例化)。

通过对GetInst()方法枷锁或者对Single2类进行加锁,可以解决该风险,每个线程在进入方法前,都要等到别的线程都离开此方法,不会有两个线程同时进入此方法。

//懒汉式指针
//即使创建指针类型也存在问题
class SinglePointer
{
private:SinglePointer() { }SinglePointer(const SinglePointer&) = delete;SinglePointer& operator=(const SinglePointer&) = delete;
public:static SinglePointer *GetInst(){if (single != nullptr){return single;}s_mutex.lock();if (single != nullptr){s_mutex.unlock();return single;}single = new SinglePointer();s_mutex.unlock();return single;}
private:static SinglePointer *single;static mutex s_mutex;
};

该段代码块通过双重检验枷锁进行加锁,避免了直接加锁造成的问题:每次去获取对象都需要先获取锁,并发性能非常地差。

双重检验枷锁:

  • 如果已经实例化了,则不需要加锁,直接返回实例化对象

  • 如果没有实例化对象则加锁,然后再判断一次有没有实例化

  • 如果实例化了就解锁并返回实例化对象

  • 如果没有实例化就初始化实例化对象,并解锁返回实例化对象

进行测试

//懒汉式
//在类的cpp文件定义static变量
SinglePointer *SinglePointer::single = nullptr;
std::mutex SinglePointer::s_mutex;void thread_func_lazy(int i)
{cout << "this is lazy thread " << i << endl;cout << "inst is " << SinglePointer::GetInst() << endl;
}
void test_singlelazy()
{for (int i = 0; i < 3; i++){thread tid(thread_func_lazy, i);tid.join();}//何时释放new的对象?造成内存泄漏
}
int main(){test_singlelazy();
}

输出为

this is lazy thread 0
inst is 0xbc1700
this is lazy thread 1
inst is 0xbc1700
this is lazy thread 2
inst is 0xbc1700

尽管多线程下懒汉式可能会创建多个Single2类实例的问题被解决,但无论懒汉式还是饿汉式,都有一个共同的问题需要解决:什么时候释放内存?多线程下多次delete也会造成崩溃。

3)智能指针方法

使用智能指针方法自动回收内存的机制设计单例类

//利用智能指针解决释放问题
class SingleAuto
{
private:SingleAuto() { }SingleAuto(const SingleAuto&) = delete;SingleAuto& operator=(const SingleAuto&) = delete;
public:~SingleAuto(){cout << "single auto delete success " << endl;}static std::shared_ptr<SingleAuto> GetInst() {if (single != nullptr) {return single;}s_mutex.lock();if (single != nullptr) {s_mutex.unlock();return single;}single = std::make_shared<SingleAuto>();s_mutex.unlock();return single;}
private:static std::shared_ptr<SingleAuto> single;static mutex s_mutex;
};

SingleAuto类的GetInst()返回std::shared_ptr<SingleAuto>类型的变量single。因为single是静态成员变量,所以会在进程结束时被回收。智能指针被回收时会调用内置指针类型的析构函数,从而完成内存的回收。

测试

// 智能指针方式
std::shared_ptr<SingleAuto> SingleAuto::single = nullptr;
mutex SingleAuto::s_mutex;
void test_singleauto()
{auto sp1 = SingleAuto::GetInst();auto sp2 = SingleAuto::GetInst();cout << "sp1  is  " << sp1 << endl;cout << "sp2  is  " << sp2 << endl;//此时存在隐患,可以手动删除裸指针,造成崩溃// delete sp1.~SingleAuto();
}
int main(){test_singleauto();
}

输出:

sp1  is  0x1174f30
sp2  is  0x1174f30

智能指针方式不存在内存泄漏,但是有一个隐患:单例类的析构函数是公有成员,如果被人手动调用会存在崩溃问题,比如将上边测试中的注释打开,程序会崩溃

4)辅助类智能指针单例模式

将析构函数私有化,在构造智能指针时指定删除器,通过传递一个辅助类或者辅助函数帮助智能指针回收内存时调用指定的析构函数。因为析构函数私有化以后,智能指针在引用计数归零后无法调用对象的析构函数进行销毁。所以必须指定一个删除器,该删除器是单例类的友元类,可以访问单例类的私有或公有成员,可以通过删除器间接调用单例类的析构函数。

// safe deletor
//该类定义仿函数调用SingleAutoSafe析构函数
class SingleAutoSafe;class SafeDeletor
{
public:void operator()(SingleAutoSafe *sf){cout << "this is safe deleter operator()" << endl;delete sf;}
};class SingleAutoSafe
{
private:SingleAutoSafe() {}~SingleAutoSafe(){cout << "this is single auto safe deletor" << endl;}SingleAutoSafe(const SingleAutoSafe &) = delete;SingleAutoSafe &operator=(const SingleAutoSafe &) = delete;//定义友元类,通过友元类调用该类析构函数friend class SafeDeletor;
public:static std::shared_ptr<SingleAutoSafe> GetInst(){if (single != nullptr){return single;}s_mutex.lock();if (single != nullptr){s_mutex.unlock();return single;}//额外指定删除器single = std::shared_ptr<SingleAutoSafe>(new SingleAutoSafe, SafeDeletor());//也可以指定删除函数// single = std::shared_ptr<SingleAutoSafe>(new SingleAutoSafe, SafeDelFunc);s_mutex.unlock();return single;}
private:static std::shared_ptr<SingleAutoSafe> single;static mutex s_mutex;
};

如果是提前声明SafeDeletor ,而先定义SingleAutoSafe,会造成 incomplete type(类型不完整)的错误,因为被提前声明的类,可以在后面定义,但在声明类前面定义的其他类中使用声明类时,只能使用声明类的指针,而不能创建声明类的实例。

SafeDeletor类中重载了(),实现类模拟函数的作用。

SafeDeletor要写在SingleAutoSafe上边,并且SafeDeletor要声明为SingleAutoSafe类的友元类,这样就可以访问SingleAutoSafe的析构函数了。在构造single时制定了SafeDeletor(),single在回收时,会调用仿函数SafeDeletor(),从而完成内存的销毁。同时,SingleAutoSafe的析构函数为私有,无法被外界显式调用。

5)通用的单例模板类

通过声明单例的模板类,然后继承这个单例模板类的所有类就是单例类了,可以达到泛型编程提高效率的目的

template <typename T>
class Single_T
{
protected:Single_T() = default;Single_T(const Single_T<T> &st) = delete;Single_T &operator=(const Single_T<T> &st) = delete;~Single_T(){cout << "this is auto safe template destruct" << endl;}
public:static std::shared_ptr<T> GetInst(){if (single != nullptr){return single;}s_mutex.lock();if (single != nullptr){s_mutex.unlock();return single;}//额外指定删除器single = std::shared_ptr<T>(new T, SafeDeletor_T<T>());//也可以指定删除函数// single = std::shared_ptr<SingleAutoSafe>(new SingleAutoSafe, SafeDelFunc);s_mutex.unlock();return single;}
private:static std::shared_ptr<T> single;static mutex s_mutex;
};//模板类的static成员要放在h文件里初始化
template <typename T>
std::shared_ptr<T> Single_T<T>::single = nullptr;
template <typename T>
mutex Single_T<T>::s_mutex;

模板类的静态成员变量要在头文件中初始化,而非模板类的静态成员变量一般在cpp文件中初始化。

应用:定义一个网络的单例类,继承上述模板类,并将构造和析构设置为私有,同时设置友元保证自己的析构和构造可以被友元类调用.

class SingleNet : public Single_T<SingleNet>
{
private:SingleNet() = default;SingleNet(const SingleNet &) = delete;SingleNet &operator=(const SingleNet &) = delete;~SingleNet() = default;friend class SafeDeletor_T<SingleNet>;friend class Single_T<SingleNet>;
};

删除器SafeDeletor_T和单例模板类Single_T都是模板,需要提前定义。

测试

void test_singlenet()
{auto sp1 = SingleNet::GetInst();auto sp2 = SingleNet::GetInst();cout << "sp1  is  " << sp1 << endl;cout << "sp2  is  " << sp2 << endl;
}

5)总结

1. 为什么要有单例模式?

使用单例模式的原因

  • 资源控制:单例模式可以用来控制系统中的资源,例如数据库连接池或线程池,确保这些关键资源不会被过度使用。

  • 内存节省:当需要一个对象进行全局访问,但创建多个实例会造成资源浪费时,单例模式可以确保只创建一个实例,节省内存。

  • 共享:单例模式允许状态或配置信息在系统的不同部分之间共享,而不需要传递实例。

  • 延迟初始化:单例模式支持延迟初始化,即实例在首次使用时才创建,而不是在类加载时。

  • 一致的接口:单例模式为客户端提供了一个统一的接口来获取类的实例,使得客户端代码更简洁。

  • 易于维护:单例模式使得代码更易于维护,因为所有的实例都使用相同的实例,便于跟踪和修改变更。

单例模式的应用场景

  • 配置管理器:在应用程序中,配置信息通常只需要读取一次,并全局使用。单例模式用于确保配置管理器只被实例化一次。

  • 日志记录器:一个系统中通常只需要一个日志记录器来记录所有的日志信息,使用单例模式可以避免日志文件的重复写入。

  • 数据库连接池:数据库连接是一种有限的资源,使用单例模式可以确保数据库连接池的唯一性,并且能够重用连接,减少连接创建和销毁的开销。

  • 线程池:类似于数据库连接池,线程池也是有限的资源,使用单例模式可以避免创建过多的线程,提高应用程序的并发性能。

  • 任务调度器:在需要全局调度和管理的场景下,如定时任务调度器,单例模式提供了一个集中的管理方式。

  • 网站的计数器:一般也是采用单例模式实现,否则难以同步。

2. 单例模式中GetInst()为什么是静态的?

1)静态方法可以通过类名直接访问,无需创建类的实例。这样可以方便地获取唯一实例,而不需要先实例化类。 2)类的构造函数已经被私有化,无法直接实例化对象,智能通过定义静态成员函数的方式通过类名::方法名的方式进行构造访问 3)静态局部变量的方式是线程安全的,能确保在多线程环境下也只会创建一个实例

相关文章:

网络编程(13)——单例模式

十三、day13 今天学习如何单例模式实现逻辑层的设计。内容包括服务器如何能捕获信号使其安全退出、单例模标类 1. 什么是单例模式&#xff1f; 单例模式&#xff08;Singleton&#xff09;&#xff0c;保证一个类仅有一个实例&#xff0c;并提供一个访问它的全局访问点&…...

基于定制开发与2+1链动模式的商城小程序搭建策略

摘要&#xff1a;本文探讨商城小程序的搭建策略&#xff0c;对比自主组建团队和第三方开发两种方式&#xff0c;强调以第三方开发模式为主的优势。阐述在第三方开发模式下&#xff0c;结合定制开发和21链动模式&#xff0c;如何搭建一款有助于企业商业模式创新与智能商业升级的…...

银河麒麟,apt 安装软件报错640Unknown Status

今天把银行麒麟的机器恢复出厂了&#xff0c;然后apt install 安装极其不稳定&#xff0c;故障现象如下图所示&#xff1a; 错误提示里面有&#xff1a; 640 Unknown Status [IP: 106.116.184.122 80] E: 无法下载 http://archive.kylinos.cn/kylin/KYLIN-ALL/pool/universe/f…...

python UNIT 3 选择与循环(2)

目录 1。循环的优化 经典优化分析&#xff1a; 未优化的代码&#xff1a; 细节分析&#xff1a; 优化后的代码&#xff1a; 优化的细节&#xff1a; 性能对比 优化的关键在于&#xff1a; 经典习题讲解&#xff1a;(紫色的解析请重点关注一下) 1。例三 个人代码解析…...

828华为云征文|部署在线文档应用程序 CodeX Docs

828华为云征文&#xff5c;部署在线文档应用程序 CodeX Docs 一、Flexus云服务器X实例介绍二、Flexus云服务器X实例配置2.1 重置密码2.2 服务器连接2.3 安全组配置2.4 Docker 环境搭建 三、Flexus云服务器X实例部署 CodeX Docs3.1 CodeX Docs 介绍3.2 CodeX Docs 部署3.3 CodeX…...

Linux的多线程(线程的创建,退出,取消请求,取消处理例程,线程属性的设置)

进程:是系统分配资源的最小单位,系统会为每一个进程分配一块独立的虚拟内存空间 线程:是系统调度的最小单位,系统不会为线程分配新的内存空间,但是线程也参与系统调度 cpu把时间片分给每一个进程&#xff0c;进程中的时间片再切分分给每一个线程&#xff0c;所以线程也会得到…...

git 本地代码关联远程仓库并推送

初始化代码仓库 如果你的本地项目还没有使用Git管理&#xff0c;首先需要在项目根目录下初始化一个Git仓库 git init添加远程仓库地址 使用 git remote add 命令添加远程仓库 git remote add origin https://github.com/username/repository.git获取远程分支信息 使用 git…...

推荐一个可以把PDF样本册转换为翻页电子书的网站

​随着互联网的普及&#xff0c;越来越多的企业和个人开始意识到线上展览的重要性。如何将实体样本册转化为线上版本&#xff0c;让更多人了解和欣赏自己的产品与服务&#xff1f; 一、网站简介 这款PDF样本册免费上传网站名为“FLBOOK”&#xff0c;致力于为广大用户提供便捷…...

【Linux 23】线程池

文章目录 &#x1f308; 一、线程池的概念&#x1f308; 二、线程池的应用场景&#x1f308; 三、线程池的实现 &#x1f308; 一、线程池的概念 线程池 (thread pool) 是一种利用池化技术的线程使用模式。 虽然创建线程的代价比创建进程的要小很多&#xff0c;但小并不意味着…...

Rust SQLite 跨平台使用

引言 Rust因其内存安全性和高性能受到越来越多开发者的青睐。在许多项目中&#xff0c;SQLite作为一种轻量级的嵌入式数据库&#xff0c;与Rust的结合为跨平台应用程序提供了强大的支持。本文将详细探讨Rust如何实现跨平台功能&#xff0c;如何在不同平台上使用Rust库&#xf…...

docker运行arm64架构的镜像、不同平台镜像构建

背景 Docker 允许开发者将应用及其依赖打包成一个轻量级、可移植的容器&#xff0c;实现“一次构建&#xff0c;到处运行”的目标。然而&#xff0c;不同的操作系统和硬件架构对容器镜像有不同的要求。例如&#xff0c;Linux 和 Windows 系统有不同的文件系统和系统调用&#…...

vue基于Spring Boot框架的高校实验室预约管理系统

目录 毕设制作流程功能和技术介绍系统实现截图开发核心技术介绍&#xff1a;使用说明开发步骤编译运行代码执行流程核心代码部分展示可行性分析软件测试详细视频演示源码获取 毕设制作流程 &#xff08;1&#xff09;与指导老师确定系统主要功能&#xff1b; &#xff08;2&am…...

Linux中find命令详解

记录linux中find命令的详细用法。 文章目录 find命令简介基本语法常用选项-name-iname-type-size-mtime,-atime,-ctime-perm-user-group-delete-exec-printand or find --help find命令简介 find 是一个搜索目录树以查找一个文件或一组文件的程序。它遍历目录树并报告与用户规…...

无水印短视频素材下载网站有哪些?十个高清无水印视频素材网站分享

你知道怎么下载无水印视频素材吗&#xff1f;今天小编就给大家推荐十个高清无水印视频素材下载的网站&#xff0c;如果你也是苦于下载高清无水印的短视频素材&#xff0c;赶紧来看看吧&#xff5e; 1. 稻虎网 首推的是稻虎网。这个网站简直就是短视频创作者的宝库。无论你需要…...

SpringBoot+Activiti7工作流入门实例

目录 文章目录 目录准备Activiti建模工具1、BPMN-js在线设计器1.1 安装1.2 使用说明1.3运行截图2、IDEA安装Activiti Designer插件2.1安装插件2.2 设置编码格式防止中文乱码2.3 截图简单工作流入门实例1. 新建Spring Boot工程2. 引入Activiti相关依赖添加版本属性指定仓库添加依…...

Azure OpenAI检索增强微调:使用 GPT-4o 对 GPT-4o mini 进行微调,以适应特定领域的应用

定制是关键&#xff01; 生成式人工智能对企业最有影响力的应用之一是创建自然语言界面&#xff0c;这些界面经过定制&#xff0c;可以使用特定领域和用例数据来提供更好、更准确的响应。这意味着回答有关特定领域的问题&#xff0c;例如银行、法律和医疗领域。 我们经常谈…...

ISP Pipeline

系列文章目录 文章目录 系列文章目录前言一、RAW域二、RGB域三、YUV域总结 前言 一、RAW域 黑电平校正&#xff08;BLC&#xff09;数字增益调整&#xff08;DGain&#xff09;自动白平衡&#xff08;AWB&#xff09;局部色调映射&#xff08;LTM&#xff09;坏点修复&#xf…...

< IDE编程环境配置>

IDE编程环境配置 LIB&#xff0c;DLL区别 我们在写项目时会链接&#xff08;调用&#xff09;第3方库&#xff0c;或者比如在vs的解决方案solution创建项目project时&#xff0c;不仅可以开发可执行程序exe&#xff08;可单独运行&#xff09;&#xff08;windows/控制台 应用…...

Golang | Leetcode Golang题解之第448题找到所有数组中消失的数字

题目&#xff1a; 题解&#xff1a; func findDisappearedNumbers(nums []int) (ans []int) {n : len(nums)for _, v : range nums {v (v - 1) % nnums[v] n}for i, v : range nums {if v < n {ans append(ans, i1)}}return }...

【Spring Boot 入门三】Spring Boot与数据库集成 - 构建数据驱动的应用

一、引言 在之前的文章中&#xff0c;我们已经对Spring Boot有了初步的认识&#xff0c;了解了如何构建第一个Spring Boot应用&#xff0c;以及如何通过配置文件来掌控应用的设置。这些知识为我们进一步探索Spring Boot与数据库的集成奠定了坚实的基础。 数据库是现代应用的核…...

Web 服务器与动态脚本语言通信的接口协议有哪些

Web 服务器与动态脚本语言通信的接口协议主要有以下几种&#xff1a; 一、FastCGI&#xff08;Fast Common Gateway Interface&#xff09; 特点&#xff1a;使用持久进程处理请求&#xff0c;减少了进程启动和关闭的开销&#xff0c;提高了性能和可扩展性。多个请求可由同一个…...

ESXI识别服务器磁盘,虚拟机显示无效

ESXI识别服务器磁盘&#xff0c;虚拟机显示无效 系统意外断电识别不到磁盘的情况下可以管理-》硬件-》搜索磁盘名称&#xff0c;选择切换直通&#xff0c;则虚拟机正常。...

【C++】 vector 迭代器失效问题

【C】 vector 迭代器失效问题 一. 迭代器失效问题分析二. 对于vector可能会导致其迭代器失效的操作有&#xff1a;1. 会引起其底层空间改变的操作&#xff0c;都有可能是迭代器失效2. 指定位置元素的删除操作--erase3. Linux下&#xff0c;g编译器对迭代器失效的检测并不是非常…...

【Spring基础3】- Spring的入门程序

目录 3-1 Spring的下载3-2 Spring的 jar 包3-3 第一个 Spring程序第一步&#xff1a;添加spring context的依赖&#xff0c;pom.xml配置如下第二步&#xff1a;添加junit依赖第三步&#xff1a;定义bean&#xff1a;User第四步&#xff1a;编写spring的配置文件&#xff1a;bea…...

golang学习笔记22-面向对象(四):接口【重要】

本节也是GO核心部分&#xff0c;很重要。 注&#xff1a;由于导包语句已经在19讲&#xff08;笔记19&#xff1a;面向对象的引入&#xff09;展示过了&#xff0c;所以这里就不展示了。 一、定义与实现 (1)接口中可以定义一组方法&#xff0c;但不需要实现&#xff0c;不需要…...

SpringBoot技术栈:打造下一代网上租赁系统

第2章 关键技术简介 2.1 Java技术 Java是一种非常常用的编程语言&#xff0c;在全球编程语言排行版上总是前三。在方兴未艾的计算机技术发展历程中&#xff0c;Java的身影无处不在&#xff0c;并且拥有旺盛的生命力。Java的跨平台能力十分强大&#xff0c;只需一次编译&#xf…...

Vue-Lecture1-Notes

渐进式框架 Vue 被称为“渐进式框架”&#xff0c;是因为它允许开发者根据项目的需求逐步引入和使用其功能&#xff0c;而不需要一次性使用整个框架。简单来说&#xff0c;Vue 提供了从简单到复杂的功能层次&#xff0c;可以灵活选择使用。 按需使用&#xff1a;Vue 的核心功能…...

网上租赁系统开发:SpringBoot实践与应用

第2章 关键技术简介 2.1 Java技术 Java是一种非常常用的编程语言&#xff0c;在全球编程语言排行版上总是前三。在方兴未艾的计算机技术发展历程中&#xff0c;Java的身影无处不在&#xff0c;并且拥有旺盛的生命力。Java的跨平台能力十分强大&#xff0c;只需一次编译&#xf…...

User-Agent在WebMagic爬虫中的重要性

对于需要从网站上抓取数据的开发者来说&#xff0c;WebMagic是一个强大的工具。它是一个简单灵活的Java爬虫框架&#xff0c;用于抓取网页数据。在爬虫技术中&#xff0c;User-Agent&#xff08;用户代理&#xff09;是一个关键的HTTP请求头&#xff0c;它告诉服务器关于客户端…...

如何解决 Docker 下载 mcr.microsoft.com 镜像慢的办法

一、介绍 MCR(Miscrosoft Container Registry) 加速器&#xff0c;助你在中国大陆急速下载 netcore 相关的 docker 镜像。二、解决办法。     1、如何使用       至少三种方法进行加速&#xff1a;         使用 docker-mcr &#xff08;推荐&#xff09;  …...

找网站建设企业/西安seo服务公司排名

&#xfeff;您不需要集成任何内容。您可以使用pygame屏幕作为GUI。公平警告&#xff1a;它没有内置功能&#xff0c;如按钮或弹出窗口。每次都需要硬编码。通过集成&#xff0c;如果你的意思是python代码的结果应该显示在屏幕上&#xff0c;那么这将发生。如果要将另一个pytho…...

无锡新吴区建设局网站/百度公司名称

Qt 实现文件校验码生成器&#xff08;内附源码&#xff09; 该软件是基于 CertUtil 的一个文件文件校验码生成&#xff0c;旨在提高下载程序的一个安全系数&#xff0c;防止黑客攻击网站后&#xff0c;将携带病毒的程序放在下载链接上&#xff0c;当用户使用程序时&#xff0c…...

安康网站定制厂家/鞋子软文推广300字

这篇文章主要介绍了关于php面向对象之类与实例化对象&#xff0c;有着一定的参考价值&#xff0c;现在分享给大家&#xff0c;有需要的朋友可以参考一下类声明[修饰符] class 类名{[属性][方法]}注意事项&#xff1a;1)类名遵循大写开头的驼峰命名规范2)花括号的开始、结束标记…...

做网站的去哪找客户/百度seo排名教程

化到这一步&#xff0c;我们就将原来一个数据会非常大的A^B,变成了很多项的乘积。编程实现的时候&#xff0c;我们只需走一遍B的二进制位&#xff0c;并用一个变量a记录当前二进制位的权值&#xff0c;判断当前bi的值&#xff0c;然后将结果乘起来取模即可。快速幂取模通过将指…...

网站如何做百度才会收录/厦门网站设计公司

原题链接 思路&#xff1a; ASCII码中32代表的就是空格’ 。此题不需要加逗号或者加空格 #include <stdio.h>int main() {printf("%c%c%c%c%c%c%c%c%c%c%c%c\n",73,32,99,97,110,32,100,111,32,105,116,33);return 0; }...

网站制作的软件有哪些/百度首页网站推广多少钱一年

一、冒泡排序 1、Explanation And Steps&#xff08;解释的步骤&#xff09; 冒泡排序&#xff08;Bubble Sort&#xff09;也是一种简单直观的排序算法。它重复地走访过要排序的数列&#xff0c;一次比较两个元素&#xff0c;如果他们的顺序错误就把他们交换过来。走访数列的…...