[toc]

开篇:为什么要系统的学习和记录这个?

  1. 在实际的游戏开发中对于性能有着很高的要求,即可能给你的业务或者行为链的时间耗费只有不到 1ms 的时间
  2. 业界通常使用的 ECS 架构可以做到将 逻辑 - 组件库 - 渲染 进行分离,其实这之中的每一个点都可以使用 ECS 来进行重构.
  3. 那么为什么 ECS 有如此的魅力呢?因为 ECS 并非一个简单的架构问题,如果去看一些开源的 ECS 库,你会发现它们在存储 Component 时会使用 Sparser 进行命名,而这个 Sparser 其实就是并发编程中一个很常见的结构思路,比如: 稀疏数据结构稠密数据结构AOSSOA
  4. 而这些其实都是 High Performence C/C++/Csharp 的组成部分,而 HPC 中实际又包含了 CUDA 一类 GPGPU 和异构计算的知识,而后者在游戏研发中的体现就是 ComputeShader , 虽然现在移动端不普及,但是谁知道几年后呢?
  5. 所以必须学!

# join 和 detach

# Join()

Joins 代表了一个 发起线程目标线程 间的同步点
它会将发起线程锁定,直到目标线程执行完毕

# detach

detach 是无阻赛的线程执行,即 不阻塞主线程 ,而且可以继续运行线程

# detach 可能出现的问题

detach 在发起线程被终结后,仍然可以自行运行,所以就会导致有可能出现引用访问的异常,如下述代码

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
void func_2(int& x) {
while (true) {
try {
std::cout << x << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}
catch (...) {
std::cout << "this is a runtime error" << std::endl;
throw std::runtime_error("this is a runtime error");
}
}
}

void func_1() {
int x = 5;
std::thread thread_2(func_2, std::ref(x));
//由于调用了detach() thread_2的生命周期不会随着func_1的消亡而消亡
//所以当thread_2尝试在func_1消亡后访问x,就会抛异常
thread_2.detach();
std::this_thread::sleep_for(std::chrono::milliseconds(5000));
std::cout << "thread_1 finished execution\n";
}

void run() {
std::thread thread_1(func_1);
thread_1.join();
}

int main() {
run();
return 1;
}

注释中已经写了

1
2
由于调用了detach() thread_2的生命周期不会随着func_1的消亡而消亡
所以当thread_2尝试在func_1消亡后访问x,就会抛异常

其中两个线程与主线程之间的关系如下:

func_1 线程在某个时刻唤起了 func_2 线程,然后 func_2 使用 detach 方式来执行线程,所以当 func_1 随生命周期消亡后, func_2 线程仍然在执行