《C++ Primer Plus》第16章:string类和标准模板库(11)
其他库
C++ 还提供了其他一些类库,它们比本章讨论前面的例子更为专用。例如,头文件 complex 为复数提供了类模板 complex,包含用于 float、long 和 long double 的具体化。这个类提供了标准的复数运算及能够处理复数的标准函数。C++11 新增的头文件 random 提供了更多的随机数功能。
第14章介绍了头文件 valarry 提供的模板类 valarray。这个类模板被设计成用于表示数值数组,支持各种数值数组操作,例如将两个数组的内容相加、对数组的每个元素应用数学函数以及对数组进行线性代数运算。
vector、valarray 和 array
你可能会问,C++ 为何提供三个数组模板:vector、valarray 和 array。这些类是由不同的小组开发的,用于不同的目的。vector 模板类是一个容器类和算法系统的一部分,它支持面向容器的操作,如排序、插入、重新排列、搜索、将数据转移到其他容器中等。而 valarray 类模板是面向数值计算的,不是 STL 的一部分。例如,它没有 push_back() 和 insert() 方法,但为很多数学运算提供了一个简单、直观的接口。最后 array 是为替代内置数组而设计的,它通过提供更好、更安全的接口,让数组更紧凑,效率更高。Array 表示长度固定的数组,因此不支持 push_back() 和 insert(),但提供了多个 STL 方法,包括 begin()、end()、rbegin() 和 rend(),这使得很容易将 STL 算法用于 array 对象。
例如,假设有如下声明:
vector<double> vedl(10), ved2(10), ved3(10);
array<double, 10> vod1, vod2, vod3;
valarray<double> vad1(10), vad2(10), vad3(10);
同时,假设 ved1、ved2、vod1、vod2、vad1 和 vad2 都有合适的值。要将两个数组中第一个元素的和赋给第三个数组的第一个元素,使用 vector 类时,可以这样做:
transform(ved1.begin(), ved1.end(), ved2.begin(),, ved3.begin(), plus<double>() );
对于 array 类,也可以这样做:
transfrom(vod1.begin(), vod1.end(), vod2.begin(), vod3.begin(), plus<double>() );
然而,valarray 类重载了所有算术运算符,使其能够用于 valarray 对象,因此您可以这样做:
vad3 = vad1 + vad2; // + overloaded
要将数组中每个元素的值扩大 2.5 倍,STL 方法如下:
transform(ved3.begin(), ved3.end(), ved3.begin(), bind1st(multiplies<double>(), 2.5) );
valarray 类重载了将 valarray 对象乘以一个值的运算符,还重载了各种组合赋值运算符,因此可以采取下列两种方法之一:
vad3 = 2.5 * vad3; // * overloaded
vad3 *= 2.5; // *= overloaded
假设您要计算数组中每个元素的自然对数,并将计算结果存储到另一个数组的相应元素中,STL 方法如下:
transform(ved1.begin(), ved1.end(), ved3.begin(), log);
valarray 类重载了这种数学函数,使之接受一个 valarray 参数,并返回一个 valarray 对象,因此您可以这样做:
vad3 = log(vad1); // log() overloaded
也可以使用 apply() 方法,该方法也适用于非重载函数:
vad3 = vad1.apply(log);
方法 apply() 不修改调用对象,而是返回一个包含结果的新对象。
执行多步计算时,valarray 接口的简单性将更为明显:
vad3 = 10.0 * ( (vad1 + vad2) / 2.0 + vad1 * cos(vad2) );
有关使用 STL vector 来完成上述计算的代码留给您去完成。
valarray 类还提供了方法 sum()(计算 valarray对象中所有元素的和)、size() (返回元素数)、max()(返回最大的元素值)和 min() (返回最小的元素值)。
正如您看到的,对于数学运算而言,valarray 类提供了比 vector 更清晰的表示方式,但通用性更低。valarray 类确实有一个 resize() 方法,但不能像使用 vector 的 push_back 时那样自动调整大小。没有支持插入、排序、搜索等操作的方法。总之,与 vector 类相比,valarray 类关注的东西更少,但这使得它的接口更简单。
valarray 的接口更简单是否意味着性能更高呢?在大多数情况下,答案是否定的。简单表示法通常是使用类似于您处理常规数组时使用的循环实现的。然而,有些硬件设计允许在执行 vector 操作时,同时将一个数组中的值加载到一组寄存器中,然后并行地进行处理。从原则上说,valarray 操作也可以实现成利用这样的设计。
可以将 STL 功能用于 valarray 对象吗?通过回答这个问题,可以快速地复习一些 STL 原理。假设有一个包含 10 个元素的 valarray<double> 对象:
valarray<double> vad(10);
使用数字填充该数组后,能够将 STL sort() 函数用于该数组吗?valarray 类没有 begin() 和 end() 方法,因此不能将它们用作指定区间的参数:
sort(vad.begin(), vad.end()); // NO, no begin(), end()
另外,vad 是一个对象,而不是指针,因此不能像处理常规数组那样,使用 vad 和 vad + 10 作为区间参数,即下面的代码不可行:
sort(vad, vad + 10); // NO, vad an object, not an address
可以使用地址运算符:
sort(&vad[0], &vad[10]); // maybe?
但 valarray 没有定义下标超过尾部一个元素的行为。这并不意味着使用 &vad[10] 不可行。事实上,使用 6 中编译器测试上述代码时,都是可行的;但这确实意味着可能不可行。为让上述代码不可行,需要一个不太可能出现的条件,如让数组与预留给堆的内存块相邻。然而,如果 3.85 亿的交易金命悬与您的代码,您可能不想冒代码出现问题的风险。
为解决这种问题,C++11 提供了接受 valarray 对象作为参数的模板函数 begin() 和 end()。因此,您将使用 begin(vad) 而不是 vad.begin。这些函数返回的值满足 STL 区间需求:
sort(begin(vad), end(vad) ); // C++11 fix!
下面的程序演示了 vector 和 valarray 类各自的优势。它使用 vector 的 push_back() 方法和自动调整大小的功能来收集数据,然后对数字进行排序后,将它们从 vector 对象复制到一个同样大小的 valarray 对象中,再执行一些数学运算。
// valvect.cpp -- comparing vector and valarray#include <ios>
#include<iostream>
#include<valarray>
#include<vector>
#include<algorithm>int main() {using namespace std;vector<double> data;double temp;cout << "Enter numbers (<=0 to quit):\n";while(cin >> temp && temp > 0){data.push_back(temp);}sort(data.begin(), data.end());int size = data.size();valarray<double> numbers(size);int i;for(i=0; i<size; i++){numbers[i] = data[i];}valarray<double> sq_rts(size);sq_rts = sqrt(numbers);valarray<double> results(size);results = numbers + 2.0 * sq_rts;cout.setf(ios_base::fixed);cout.precision(4);for (i=0; i< size; i++){cout.width(8);cout << numbers[i] << ": ";cout.width(8);cout << results[i] << endl;}cout << "done\n";return 0;
}
下面是程序的运行情况:
Enter numbers (<=0 to quit):
3.3 1.8 5.2 10 14.4 21.6 26.9 01.8000: 4.48333.3000: 6.93325.2000: 9.760710.0000: 16.324614.4000: 21.989521.6000: 30.895226.9000: 37.2730
done
除前面讨论的外,valarray 类还有很多其他特性。例如,如果 numbers 是一个 valarray<double> 对象,则下面的语句将创建一个 bool 数组,其中 vbool[i] 被设置为 numbers[i] > 9 的值,即 true 或 false:
valarray<bool> vbool = numbers > 9;
还有扩展的下标指定版本,来看其中的一个——slice 类。slice 类对象可用作数组索引,在这种情况下,它表的不是一个值而是一组值。slice 对象被初始化为三个整数值:起始索引、索引数和跨距。起始索引是第一个被选中的元素的索引,索引数指出要选择多少个元素,跨距表示元素之间的间隔。例如,slice(1,4,3) 创建的对象标识选择 4 个元素,它们的索引分别是1、4、7、10。也就是说,从起始索引开始,加上跨距得到下一个元素的索引,依此类推,直到选择了 4 个元素。如果 varint 是一个 valarray<int>对象,则下面的语句将把第1、4、7、10个元素都设置为10:
varint[slice(1,4,3)] = 10; // set selected elements to 10
这种特殊的下标指定功能让您能够使用一个一维 valarray 对象来表示二维数据。例如,假设要表示一个4行3列的数组,可以将信息存储在一个包含12个元素的valarray对象中,然后使用一个 slice(0,3,1)对象作为下标,来表示元素0、1、2,即第1行。同样,下标 slice(0,4,3)表示元素0、3、6、9,即第一列。下面的程序演示了 slice 的一些特性。
// vslice.cpp -- using valarray slices
#include<iostream>
#include<valarray>
#include<cstdlib>const int SIZE = 12;
typedef std::valarray<int> vint; // simplify declaratrions
void show(const vint & v, int cols);int main(){using std::slice; // from <valarray>using std::cout;vint valint(SIZE); // think of as 4 rows of 3int i;for (i=0; i < SIZE; i++){valint[i] = std::rand()%10;}cout << "Original array:\n";show(valint, 3); // show in 3 columnsvint vcol(valint[slice(1,4,3)]); // extract 2nd columncout << "Second column:\n";show(vcol, 1); // show in 1 columnvint vrow(valint[slice(3,3,1)]); // extract 2nd rowcout << "Second row:\n";show(vrow, 3);valint[slice(2,4,3)] = 10; // assign 10 to 3 columncout << "Set last column to 10:\n";show(valint, 3);cout << "Set first column to sum of next two:\n";// + not defined for slices, so convert to valarray<int>valint[slice(0,4,3)] = vint(valint[slice(1,4,3)]) + vint(valint[slice(2,4,3)]);show(valint, 3);return 0;
}void show(const vint & v, int cols){using std::cout;using std::endl;int lim = v.size();for (int i = 0; i < lim; i++){cout.width(3);cout << v[i];if(i%cols == cols -1 )cout << endl;elsecout << ' ';}if(lim%cols!=0)cout << endl;
}
对于 valarray 对象(如 valint)和单个 int 元素(如 valint[1]),定义了运算符++;但正如程序指出的,对于使用 slice 下标指定的 valarray 单元,如 valint[slice(1,4,3)],并没有定义运算符+。因此程序使用 slice 指定的元素创建一个完整的 valint 对象,以便能够执行加法运算:
vint(valint[slice(1,4,3)]) // calls a slice-based constructor
valarray 类提供了用于这种目的的构造函数。
下面是该程序的运行情况:
Original array:1 7 40 9 48 8 24 5 5
Second column:7985
Second row:0 9 4
Set last column to 10:1 7 100 9 108 8 104 5 10
Set first column to sum of next two:17 7 1019 9 1018 8 1015 5 10
由于元素值是使用 rand() 设置的,因此不同的 rand() 实现将设置不同的值。
另外,使用 gslice 类可以表示多维下标,但上述内容应足以让您对 valarray 有一定的了解。
模板 initializaer_list(C++11)
模板 initializer_list 是 C++11 新增的。您可使用初始化列表语法将 STL 容器初始化为一系列值:
std::vector<double> payments { 45.99, 39.23, 19.95, 89.01};
这将创建一个包含 4 个元素的容器,并使用列表中的 4 个值来初始化这些元素。这之所以可行,是因为容器类现在包含将 initializer_list<T> 作为参数的构造函数。例如,vector<double> 包含一个将 initializer_list<double> 作为参数的构造函数,因此上述声明与下面的代码等价:
std::vector<double> payments({45.99, 39.23, 19.95, 89.01});
这里显式地将列表指定为构造函数参数。
通常,考虑到 C++11 新增的通用初始化语法,可使用表示法{} 而不是 () 来调用类构造函数:
shared_ptr<double> pd {new double}; // ok to use {} instead of ()
但如果类也有接受 initializer_list 作为参数的构造函数,这将带来问题:
std::vector<int> vi{10}; // ??
这将调用哪个构造函数呢?
std::vector<int> vi(10); // case A: 10 uninitialized elements
std::vector<int> vi({10}); // case B: 1 element set to 10
答案是,如果类有接受 initializer_list 作为参数的构造函数,则使用语法 {} 将调用该构造函数。因此,在这个示例中,对应的是情形 B。
所有 initializer_list 元素的类型都必须相同,但编译器将进行必要的转换:
std::vector<double> payments {45.99, 39.23, 19, 89};
// same as std::vector<double> payments { 45.99, 39.23, 19.0, 89.0 };
在这里,由于 vector 的元素类型为 double,因此列表的类型为 initializer_list<double>,所以 19 和 89 将被转换为 double。
但不能进行隐式的缩窄变换:
std::vector<int> values = {10, 8, 5.5 }; // narrowing, compile-time error
在这里,元素类型为 int,不能隐式地将 5.5 转换为 int。
除非类要用于处理长度不同的列表,否则让它提供接受 initializer_list 作为参数地构造函数没有意义。例如,对于存储固定数目值得类,您不想提供接受 initializer_list 作为参数的构造函数。在下面的声明中,类包含三个数据成员,因此没有提供 Initializer_list 作为参数的构造函数:
class Position {
private:int x;int y;int z;
public:Position(int xx = 0, int yy = 0, int zz = 0 ): x(xx), y(yy), z(zz) {}// no initializer_list constructor...
};
这样,使用语法 {} 时将调用构造函数 Position(int, int, int):
Position A = {20, -3}; // uses Position(20,-3,0);
使用 initializer_list
要在代码中使用 initializer_list 对象,必须包含头文件 initializer_list。这个模板类包含成员函数 begin() 和 end(),您可使用这些函数来访问列表元素。它还包含成员函数 size(),该函数返回元素数。下面的程序时一个简单的 Initializer_list 使用示例,它要求编译器支持 C++11 新增的 initializer_list。
// ilist.cpp -- use initializer_list (C++111 feature)#include<iostream>
#include<initializer_list>double sum(std::initializer_list<double> il);
double average(const std::initializer_list<double> & ril);int main(){using std::cout;cout << "List 1: sum = " << sum({2, 3, 4})<< ", ave = " << average({2, 3, 4}) << '\n';std::initializer_list<double> dl = {1.1, 2.2, 3.3, 4.4, 5.5};cout << "List 2: sum = " << sum(dl)<< ", ave = " << average(dl) << '\n';dl = {16.0, 25.0, 36.0, 46.0, 64.0};cout << "List 3: sum = " << sum(dl)<< ", ave = " << average(dl) << '\n';return 0;}double sum(std::initializer_list<double> il){double tot = 0;for (auto p = il.begin(); p != il.end(); p++){tot += *p;}return tot;
}double average(const std::initializer_list<double> & ril){double tot = 0;int n = ril.size();double ave = 0.0;if (n>0) {for (auto p = ril.begin(); p!=ril.end(); p++){tot += *p;}ave = tot / n;}return ave;
}
该程序的输出如下:
List 1: sum = 9, ave = 3
List 2: sum = 16.5, ave = 3.3
List 3: sum = 187, ave = 37.4
可按值传递 initializer_list 对象,也可按引用传递,如 sum() 和 average() 所示。这种对象本身很小,通常是两个指针(一个指向开头,一个指向末尾的下一个元素),也可能是一个指针和一个表示元素数的整数,因此采用的传递方式不会带来重大的性能影响。STL 按值传递它们。
函数参数可以是 initializer_lit 字面量,如 {2, 3, 4},也可以是 initializer_list 变量,如 dl。
initializer_list 的迭代器类型为 const,因此您不能修改 initializer_list 中的值:
*dl.begin() = 2011.6; // not allowed
然而,提供 initialzier_list 类的初衷旨在让您能够将一系列值传递给构造函数或其他函数。
相关文章:
《C++ Primer Plus》第16章:string类和标准模板库(11)
其他库 C 还提供了其他一些类库,它们比本章讨论前面的例子更为专用。例如,头文件 complex 为复数提供了类模板 complex,包含用于 float、long 和 long double 的具体化。这个类提供了标准的复数运算及能够处理复数的标准函数。C11 新增的头文…...
声明和定义
前言 很多编程语言的语法中都有关于声明和定义的概念,这种概念一般会应用于函数或变量的创建和使用中,但是为什么要这么做? 以C语言为例,一些书籍或教程会要求读者在程序文件开头写上函数和变量的声明,然后再在后面对…...
Python获取最小路径,查找元素在list中的坐标
# codingutf-8__author__ Jeff.xiedef t(li):pass获取最小路径def minPathSum(grid):if not grid:return 0m len(grid) #m列n len(grid[0]) #n行print(grid[0])print("m: ",m)print("n: ",n)#创建一个二维数组dp [[0]*n for _ in range(m)]print(dp) #这…...
数据采集协同架构,集成马扎克、西门子、海德汉、广数、凯恩帝、三菱、海德汉、兄弟、哈斯、宝元、新代、发那科、华中各类数控以及各类PLC数据采集软件
文章目录 前言一、采集协同架构是什么?可以做什么(数控、PLC配置采集)?二、使用步骤 1.打开软件,配置MQTT或者数据库(支持sqlserver、mysql等)存储转发消息规则2.配置数控系统所采集的参数、转…...
Allegro172版本如何用自带的功能实现快速在1MMBGA下方等距放置电容
Allegro172版本如何用自带的功能实现快速在1MMBGA下方等距放置电容 在做PCB设计的时候,在1MM中心间距的BGA背面放置电容,是非常常见的设计,如何快速把电容等距放在BGA下方,除了借助辅助工具外,在Allegro升级到了172版本的时候,可以借助本身自带的功能实现快速放置,以下图…...
一种简单的统计pytorch模型参数量的方法
nelememt()函数Tensor.nelement()->引自Tensor.numel()->引自torch.numel(input)三者的作用是相同的Returns the total number of elements in the inputtensor.返回当前tensor的元素数量利用上面的函数刚好可以统计模型的参数数量parameters()函数Module.parameters(rec…...
【PyTorch】教程:对抗学习实例生成
ADVERSARIAL EXAMPLE GENERATION 研究推动 ML 模型变得更快、更准、更高效。设计和模型的安全性和鲁棒性经常被忽视,尤其是面对那些想愚弄模型故意对抗时。 本教程将提供您对 ML 模型的安全漏洞的认识,并将深入了解对抗性机器学习这一热门话题。在图像…...
中国区使用Open AI账号试用Chat GPT指南
最近推出强大的ChatGPT功能,各大程序员使用后发出感叹:程序员要失业了 不过在国内并不支持OpenAI账号注册,多数会提示: OpenAI’s services are not available in your country. 经过一番搜索后,发现如下方案可以完…...
STM32开发(9)----CubeMX配置外部中断
CubeMX配置外部中断前言一、什么是中断1.STM32中断架构体系2.外部中断/事件控制器(EXTI)3.嵌套向量中断控制器(NIVC)二、实验过程1.CubeMX配置2.代码实现3.硬件连接4.实验结果总结前言 本章介绍使用STM32CubeMX对引脚的外部中断进…...
Nextjs了解内容
目录Next.jsnext.js的实现1,nextjs初始化2, 项目结构3, 数据注入getInitialPropsgetServerSidePropsgetStaticProps客户端注入3,CSS Modules4,layout组件5,文件式路由6,BFF层的文件式路由7&…...
从事功能测试1年,裸辞1个月,找不到工作的“我”怎么办?
做功能测试一年多了裸辞职一个月了,大部分公司都要求有自动化测试经验,可是哪来的自动化测试呢? 我要是简历上写了吧又有欺诈性,不写他们给的招聘又要自动化优先,将项目带向自动化不是一个容易的事情,很多…...
机器学习基本原理总结
本文大部分内容参考《深度学习》书籍,从中抽取重要的知识点,并对部分概念和原理加以自己的总结,适合当作原书的补充资料阅读,也可当作快速阅览机器学习原理基础知识的参考资料。 前言 深度学习是机器学习的一个特定分支。我们要想…...
JVET-AC0315:用于色度帧内预测的跨分量Merge模式
ECM采用了许多跨分量的预测(Cross-componentprediction,CCP)模式,包括跨分量包括跨分量线性模型(CCLM)、卷积跨分量模型(CCCM)和梯度线性模型(GLM)࿰…...
Session与Cookie的区别(二)
脸盲症的困扰 小明身为杂货店的店长兼唯一的店员,所有大小事都是他一个人在处理。传统杂货店跟便利商店最大的差别在哪里?在于人情味。 就像是你去菜市场买菜的时候会被说帅哥或美女,或者是去买早餐的时候老板会问你:「一样&#…...
疫情开发,软件测试行情趋势是怎么样的?
如果说,2022年对于全世界来说,都是一场极大的挑战的话;那么,2023年绝对是机遇多多的一年。众所周知,随着疫情在全球范围内逐步得到控制,无论是国际还是国内的环境,都会呈现逐步回升的趋势&#…...
Java中间件描述与使用,面试可以用
myCat 用于切分mysql数据库(为什么要切分:当数据量过大时,mysql查询效率变低) ActiveMQ 订阅,消息推送 swagger 前后端分离,后台接口调式 dubbo 阿里的面向服务RPC框架,为什么要面向服务&#x…...
[OpenMMLab]AI实战营第七节课
语义分割代码实战教学 HRNet 高分辨率神经网络 安装配置 # 选择分支 git branch -a git switch 3.x # 配置环境 conda create -n mmsegmentation python3.8 conda activate mmsegmentation pip install torch1.11.0cu113 torchvision0.12.0cu113 torchaudio0.11.0 --extra-i…...
面向对象的设计模式
"万丈高楼平地起,7种模式打地基",模式是一种规范,我们应该站在巨人的肩膀上越看越远,接下来,让我们去仔细了解了解面向对象的7种设计模式7种设计模式设计原则的核心思想:找出应用中可能需要变化之…...
里氏替换原则|SOLID as a rock
文章目录 意图动机:违反里氏替换原则解决方案:C++中里氏替换原则的例子里氏替换原则的优点1、可兼容性2、类型安全3、可维护性在C++中用好LSP的标准费几句话本文是关于 SOLID as Rock 设计原则系列的五部分中的第三部分。 SOLID 设计原则侧重于开发 易于维护、可重用和可扩展…...
【C++】右左法则,指针、函数与数组
右左法则——判断复杂的声明对于一个复杂的声明,可以用右左法则判断它是个什么东西:1.先找到变量名称2.从变量名往右看一个部分,再看变量名左边的一个部分3.有小括号先看小括号里面的,一层一层往外看4.先看到的东西优先级大&#…...
wordpress后台更新后 前端没变化的解决方法
使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…...
手游刚开服就被攻击怎么办?如何防御DDoS?
开服初期是手游最脆弱的阶段,极易成为DDoS攻击的目标。一旦遭遇攻击,可能导致服务器瘫痪、玩家流失,甚至造成巨大经济损失。本文为开发者提供一套简洁有效的应急与防御方案,帮助快速应对并构建长期防护体系。 一、遭遇攻击的紧急应…...
Lombok 的 @Data 注解失效,未生成 getter/setter 方法引发的HTTP 406 错误
HTTP 状态码 406 (Not Acceptable) 和 500 (Internal Server Error) 是两类完全不同的错误,它们的含义、原因和解决方法都有显著区别。以下是详细对比: 1. HTTP 406 (Not Acceptable) 含义: 客户端请求的内容类型与服务器支持的内容类型不匹…...
PPT|230页| 制造集团企业供应链端到端的数字化解决方案:从需求到结算的全链路业务闭环构建
制造业采购供应链管理是企业运营的核心环节,供应链协同管理在供应链上下游企业之间建立紧密的合作关系,通过信息共享、资源整合、业务协同等方式,实现供应链的全面管理和优化,提高供应链的效率和透明度,降低供应链的成…...
MySQL 8.0 OCP 英文题库解析(十三)
Oracle 为庆祝 MySQL 30 周年,截止到 2025.07.31 之前。所有人均可以免费考取原价245美元的MySQL OCP 认证。 从今天开始,将英文题库免费公布出来,并进行解析,帮助大家在一个月之内轻松通过OCP认证。 本期公布试题111~120 试题1…...
【学习笔记】深入理解Java虚拟机学习笔记——第4章 虚拟机性能监控,故障处理工具
第2章 虚拟机性能监控,故障处理工具 4.1 概述 略 4.2 基础故障处理工具 4.2.1 jps:虚拟机进程状况工具 命令:jps [options] [hostid] 功能:本地虚拟机进程显示进程ID(与ps相同),可同时显示主类&#x…...
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数 在软件开发中,单例模式(Singleton Pattern)是一种常见的设计模式,确保一个类仅有一个实例,并提供一个全局访问点。在多线程环境下,实现单例模式时需要注意线程安全问题,以防止多个线程同时创建实例,导致…...
现有的 Redis 分布式锁库(如 Redisson)提供了哪些便利?
现有的 Redis 分布式锁库(如 Redisson)相比于开发者自己基于 Redis 命令(如 SETNX, EXPIRE, DEL)手动实现分布式锁,提供了巨大的便利性和健壮性。主要体现在以下几个方面: 原子性保证 (Atomicity)ÿ…...
Cilium动手实验室: 精通之旅---13.Cilium LoadBalancer IPAM and L2 Service Announcement
Cilium动手实验室: 精通之旅---13.Cilium LoadBalancer IPAM and L2 Service Announcement 1. LAB环境2. L2公告策略2.1 部署Death Star2.2 访问服务2.3 部署L2公告策略2.4 服务宣告 3. 可视化 ARP 流量3.1 部署新服务3.2 准备可视化3.3 再次请求 4. 自动IPAM4.1 IPAM Pool4.2 …...
RabbitMQ 各类交换机
为什么要用交换机? 交换机用来路由消息。如果直发队列,这个消息就被处理消失了,那别的队列也需要这个消息怎么办?那就要用到交换机 交换机类型 1,fanout:广播 特点 广播所有消息:将消息…...
