Cpp 多线程学习笔记——3. 原子操作
原子类型和原子操作 原子操作是指在多线程编程中对共享数据的操作是不可分割、不会被中断的操作, 这意味着操作不会被其他线程干扰,不会被调度切换,要么一次性执行完成,要么完全不执行,不存在第三种状态。 原子操作可以用于避免数据竞争和保证线程安全,不过显然我们需要为安全性付出额外的性能开销。 原子类型是一种特殊的数据类型,在底层保证对原子类型变量的相关操作是原子操作, 例如对原子类型变量的读取、写入、交换、递增、递减等。 这里我们再次重复前一篇使用的例子,只是改动了共享变量的定义:使用原子类型的整数变量shared_counter而非通常的整数变量 1234567891011121314151617181920212223242526272829303132333435363738#include <atomic>#include <iostream>#include <thread>#include <vector>const int num_threads = 5;const int num_increments =...
Cpp 多线程学习笔记——2. 互斥锁
线程安全与互斥锁 在多线程编程中,由于多个线程存在共享的资源(例如全局变量等),因此可能导致相互之间产生干扰, 下面的例子可以展示这种问题(必须使用Debug模式编译,因为Release模式下可能直接优化了) 12345678910111213141516171819202122232425262728293031323334353637#include <iostream>#include <thread>#include <vector>const int num_threads = 5;const int num_increments = 10000;const int num_experiments = 20;void increment_counter(int &counter) { for (int i = 0; i < num_increments; ++i) { counter++; // 多线程同时修改共享变量 }}void...
Cpp 设计模式笔记——7. 相关概念
除了设计模式,还有几个编程概念与之相生相随,尤其在Java中被广泛的应用:面向切面编程、控制反转和依赖注入。 下面简单学习一下这几个概念,并在C++/Python中尝试应用。 面向切面编程 面向切面编程(Aspect-Oriented Programming, AOP)是一种编程范式,它旨在提高软件模块化,通过将横切关注点(Cross-cutting Concerns)与业务逻辑分离来简化程序结构。 横切关注点是指那些分散在多个模块中的功能,如日志记录、事务管理、权限控制等,这些功能虽然对多个模块都重要,但与核心业务逻辑无关。 在Java中,Spring框架广泛支持AOP,例如我们可以使用Spring AOP来添加日志记录的切面,主要代码如下 1234567@Aspectpublic class LoggingAspect { @Before("execution(* com.example.service.*.*(..))") public void logBefore(JoinPoint joinPoint) { ...
Cpp 设计模式笔记——6. 更多模式
随着软件工程的不断发展,除了经典的二十几种设计模式,还有更多设计模式在不断涌现,其中一些设计模式也是值得学习的。 对象池模式 对象池(Object Pool)是一种创建和管理对象的设计模式,特别适用于需要频繁创建和销毁对象的场景。 它通过复用对象来减少对象的创建和销毁次数,从而提高性能和资源利用率,在资源密集型的应用情景中(如数据库连接、线程、Socket连接、内存分配等),池化的思想被广泛使用。 对象池的实现示例是非常简单直观的,直接看代码即可。 与之不同的是,使用C++实现一个实用的线程池或内存池的代码细节会更加复杂,本文中不作讨论。 示例代码如下 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152#include <iostream>#include <memory>#include <vector>// 对象类class PooledObject {public: ...
Cpp 设计模式笔记——5. 行为型模式(二)
Observer...
Cpp 设计模式笔记——4. 行为型模式(一)
Chain of...
Cpp 设计模式笔记——3. 结构型模式
Adapter (适配器) 适配器模式是一种结构型设计模式,它使接口不兼容的对象能够相互合作。 假设我们已有一个现有类Adaptee,它提供接口specificRequest(),客户端希望调用接口类Target的request()方法, 两个接口无法直接相连,并且可能存在一些差异,例如函数参数顺序。 出于某些原因,我们无法更改旧有代码,那么可以选择在其中加上适配器Adapter: 适配器Adapter直接继承接口类Target,可以对客户端提供request()方法; 适配器Adapter将现有类Adaptee作为数据成员,在request()方法中实际调用Adaptee对象的specificRequest(),在传递过程中还需要处理一些差异,例如调整函数参数顺序。 示例代码如下 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748#include <iostream>#include <memory>#include...
Cpp 设计模式笔记——2. 创建型模式
Factory Method (工厂方法) 工厂方法模式是一种创建型设计模式,其在父类中提供一个创建对象的方法,允许子类决定实例化对象的类型。 我们需要定义两个抽象类:产品基类Product和工厂基类Factory,后者提供创建产品的方法createProduct()。 具体产品(ProductA、ProductB)需要继承自产品基类Product。 每一个产品都需要提供配套的工厂(FactoryA、FactoryB),工厂需要继承自工厂基类Factory,对创建产品的方法提供不同的实现。 示例代码如下 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556#include <iostream>#include <memory>// 产品接口struct Product { virtual void describe() const = 0; virtual ~Product()...
Cpp 设计模式笔记——1. 概述
有必要学一下设计模式,虽然在大部分情况下都是基于Java语言来讨论设计模式,但是面向对象的思想对于各种语言都是通用的,这一系列笔记将使用C++进行讨论。 设计原则 通常设计模式遵循七个基本原则,如下文所示。有的教程中只有六个基本原则,不含单一职责原则。 单一职责原则 每个类应该只有一个职责,即该类只有一个引起变化的原因。 这样可以减少类之间的耦合,提高系统的可维护性。 与单一职责原则相违背的极端做法是使用上帝对象,它负责了太多的职责,了解了太多的信息,这会导致代码的修改和维护非常困难。 考虑一个情景,我们需要实现一个简单的文件管理类:读取文件内容并输出到控制台,向文件中写入指定内容,我们希望在读写操作的同时在控制台中输出日志。 不符合单一职责原则的例子如下 123456789101112131415161718192021222324252627282930313233343536373839#include <fstream>#include <iostream>#include <string>class FileManager...