63、游戏服务器的基础
单核也可多线程,只不过要切换线程有额外开销,所以随着现在多核的出现,更适合多线程发展。目前CPU有4核、8核、16核,运行内存有8G、16G、32G。
可能的问题
- 死锁;
- 乱序;
- 并发访问数据;
- 低效率(为了防止前面问题的发生,需要做很多防御性工作)。
C++11带来的新概念
- 高阶接口:(async, future);
- 低阶接口:(thread, mutex);
例子:
void helloworld() {
std::cout << "hello world\n";
}
int main() {
// 一个进程本身有一个线程
std::thread t(helloworld); // 开启一个线程
std::cout << "hello main thread\n";
t.join(); // 等待此线程运行结束(线程的终结)
}
64、例子:计算量一分二
- 描述: 一千万次非常耗时的计算,可以折半分给额外的线程。注意要先创建线程去执行,再执行主线程的。否则按代码顺序执行,额外线程不仅没有利用起来,还增加了额外开销。
- 再次使用lambda: 创建线程时传入的是std::function,传统的函数名(C类型的函数指针)会自动转换为function,如果需要传入参数,或接受返回值,可以用lambda实现(Lua中有用过,所以还能理解)。
std::thread s([&value, iterBeg, iterEnd]() { value = func(iterBeg, iterEnd; }); // func为实际要执行的函数
函数内部如何知道是哪个线程在调用
- 线程id:
auto mainThreadId = std::this_thread::get_id();
,s.get_id();
- 线程暂停:
std::this_thread::sleep_for()/sleep_until();
需要引入跟时间相关的头文件<chrono>
,100毫秒使用std::chrono::milliseconds(100)
。 - 对比: 先用主线程执行一次,然后一人一半执行一次。分别打印两次耗时(
<ctime>
、auto begin = clock();
),结果比例接近2:1,但不等于,因为开一个线程是挺费时的(包括构造、清理,为环境做准备)。
65、当线程间需要共享非const的资源,(问题引出)
- 效率最高线程数量:
std::thread::hardware_concurrency();
返回效率最高的线程数量,但仅仅是个参考,实际情况一般会很复杂(服务器机器不止运行你这一个程序)。 - 描述: 一千万次计算分为三份,给三个线程去计算总和。这次传入同一个值的引用,同时传入同一个对象的引用,用来自增统计次数。然后就发现统计的次数,和计算的总和,都比正确的值要少。
- 原因: 变量在做自增操作时分三步:1、写入寄存器;2、寄存器中加1;3、写入内存。
66、(thread的构造和新问题)
- 最好的方法: 如果没有必要的话,线程间