内存池

1.避免内存的分配开销,和内存碎片

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110

#include "iostream"
#include <cstddef>
#include <string>

template<typename T,size_t PoolSize>
class ObjectPool {
//Slot结构体表示对象池中的一个槽
struct Slot {
alignas(alignof(T)) char data[sizeof(T)]; // 存储对象的内存
Slot* next; // 指向下一个槽的指针
};

Slot* freeList; // 空闲槽的链表头
Slot pool[PoolSize]; // 对象池的槽数组

public:
ObjectPool()
{
// 初始化空闲槽链表
freeList = &pool[0];
for (size_t i = 0; i < PoolSize-1; ++i) {
pool[i].next = &pool[i + 1];
}
pool[PoolSize - 1].next = nullptr; // 最后一个槽的next指针为nullptr
}

// 分配一个对象
T* Allocate() {
if (!freeList) {
return nullptr; // 如果没有空闲槽,返回nullptr
}
// 从空闲链表中取出一个槽
Slot* slot = freeList;
freeList = slot->next;

// 在槽中构造对象
T* obj = new (slot->data) T();
return obj;
}

void Deallocate(T* obj) {
if (obj) {
// 调用析构函数
obj->~T();
// 将槽添加回空闲链表
Slot* slot = reinterpret_cast<Slot*>(obj);
std::cout<< slot->data << std::endl;
slot->next = freeList;
freeList = slot;
}
}

};

class MyClass{
std::string name;
int value;
public:
MyClass():name("default"),value(0)
{
std::cout<<"MyClass constructed"<<std::endl;
}
~MyClass(){
std::cout<<"MyClass destoryed"<<std::endl;
}
void setValue(int v){value = v;}
// 修改后的 setName
void setName(const std::string& n){ name = n; }

void print() const{
std::cout<<"Name: " <<name<<",Value "<<value<<std::endl;
}
}; // <-- Add this semicolon to end the class definition

int main() {
ObjectPool<MyClass, 10> pool; // 创建一个对象池,管理10个int对象

// 分配对象
MyClass* obj1 = pool.Allocate();
MyClass* obj2 = pool.Allocate();
MyClass* obj3 = pool.Allocate();

if(obj1){
obj1->setName ("object1");
obj1->setValue(100);
obj1->print();
}

if(obj2){
obj2->setName ("object2");
obj2->setValue(200);
obj2->print();
}

if(obj3){
obj3->setName ("object3");
obj3->setValue(200);
obj3->print();
}

// 释放对象
//pool.Deallocate(obj1);
pool.Deallocate(obj2);

MyClass* obj4 = pool.Allocate();
obj4->print();

return 0;
}

Mutex 线程锁

避免C++多线程对同一数据写入和读取,产生的错误

1. 锁 std::mutex

1.创建std::mutex mtx 对象 用mtx.lock()上锁 mtx.unlock() 解锁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <iostream>
#include <thread>
#include <vector>
#include <mutex>
#include <chrono>
#include <stdexcept>

int counter = 0;
std::mutex mtx; // 保护counter

void increase(int time) {
for (int i = 0; i < time; i++) {
mtx.lock();
// 当前线程休眠1毫秒
std::this_thread::sleep_for(std::chrono::milliseconds(1));
counter++;
mtx.unlock();
}
}

int main(int argc, char** argv) {
std::thread t1(increase, 10000);
std::thread t2(increase, 10000);
t1.join();
t2.join();
std::cout << "counter:" << counter << std::endl;
return 0;
}

使用std::mutex 有个问题.如果一个线程里中有有异常,返回的异常的时候没有 解锁,导致其他线程的锁死。所以引入:std::lock_guard

2. std::lock_guard

声明lock_guard 后自动进行lock() 上锁,离开作用域后会自动unlock() 解锁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <iostream>
#include <thread>
#include <vector>
#include <mutex>
#include <chrono>
#include <stdexcept>

int counter = 0;
std::mutex mtx; // 保护counter

void increase_proxy(int time, int id) {
for (int i = 0; i < time; i++) {
// std::lock_guard对象构造时,自动调用mtx.lock()进行上锁
// std::lock_guard对象析构时,自动调用mtx.unlock()释放锁
std::lock_guard<std::mutex> lk(mtx);
// 线程1上锁成功后,抛出异常:未释放锁
if (id == 1) {
throw std::runtime_error("throw excption....");
}
// 当前线程休眠1毫秒
std::this_thread::sleep_for(std::chrono::milliseconds(1));
counter++;
}
}

void increase(int time, int id) {
try {
increase_proxy(time, id);
}
catch (const std::exception& e){
std::cout << "id:" << id << ", " << e.what() << std::endl;
}
}

int main(int argc, char** argv) {
std::thread t1(increase, 10000, 1);
std::thread t2(increase, 10000, 2);
t1.join();
t2.join();
std::cout << "counter:" << counter << std::endl;
return 0;
}

如果想在线程里执行多次的lock() 和unlock() 就需要引入std::unique_lock,离开作用域自动解锁,支持条件判断,就需要使用std::unique_lock

3. std::unique_lock

1.普通用法

1
2
3
4
5
6
7
8
9
10
11
12
13
std::mutex mtx;

void func() {
std::unique_lock<std::mutex> lock(mtx, std::defer_lock); // 不立即加锁
// 其他代码
lock.lock(); // 手动加锁
// 其他代码
lock.unlcok() //手动解锁
// 其他代码
lock.lock() //手动加锁

}

2.延迟加锁

1
2
3
4
5
6
7
8
9
std::mutex mtx;

void func() {
std::unique_lock<std::mutex> lock(mtx, std::defer_lock); // 不立即加锁
// 其他代码
lock.lock(); // 手动加锁
// ...
}

3.加锁判断

1
2
3
4
5
6
7
8
9
10
std::mutex mtx;
std::condition_variable cv;
bool ready = false;

void wait_thread() {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, []{ return ready; }); // 自动释放+重新加锁
// 继续执行
}

4. std::shared_lock() 共享锁多个线程共享 用在读取数据