C++ 多线程

一、进程与线程 进程是资源分配和调度的一个独立单位;而线程是进程的一个实体,是 CPU 调度和分配的基本单位 同一个进程中的多个线程的内存资源是共享的,各线程都可以改变进程中的变量。因此在执行多线程运算的时候要注意执行顺序 二、并行与并发 并行(parallellism)指的是多个任务在同一时刻同时在执行 并发(concurrency)是指在一个时间段内,多个任务交替进行。虽然看起来像在同时执行,但其实是交替的 三、多任务处理 多线程是多任务处理的一种特殊形式,一般情况下,有基于进程和基于线程的两种类型的多任务处理方式。 基于进程的多任务处理是程序的并发执行 基于线程的多任务处理是同一程序的片段的并发执行 四、C++11 线程管理 thread 库 C++11 提供了多线程库,使用时需要 #include <thread> 头文件,该头文件主要包含了对线程的管理类 std::thread 以及其他管理线程相关的类 每个应用程序至少有一个进程,而每个进程至少有一个主线程,除了主线程外,在一个进程中还可以创建多个子线程。每个线程都需要一个入口函数,入口函数返回退出,该线程也会退出,主线程就是以 main 函数作为入口函数的线程 主线程退出后,运行中的子线程也会被销毁 std::thread 的构造函数需要的是可调用(callable)类型,除了函数外,还可以调用 lambda 表达式、重载了 () 运算符的类的实例 把函数对象传入 std::thread 时,应传入函数名称(不带括号) 当启动一个线程后,一定要在该线程 thread 销毁前,调用 join() 或者 detach(),确定以何种方式等待线程执行结束 detach 方式,启动的线程自主在后台运行,当前的代码继续往下执行,不等待新线程结束 join 方式,等待关联的线程完成,才会继续执行 join() 后的代码 在以 detach 的方式执行线程时,要将线程访问的局部数据复制到线程的空间(使用按值传递),一定要确保线程没有使用局部变量的引用或者指针,除非你能肯定该线程会在局部作用域结束前执行结束 1、调用全局函数启动线程 #include <thread> using namespace std; void func(int i){ cout << i << endl; } int main() { for(int i = 0; i < 4; ++i){ // 创建一个线程t,第一个参数为调用的函数,第二个参数为传递的参数 thread t(func, i); // 表示允许该线程在后台运行 t....

January 4, 2022 · 2 min · Rick Cui

临时变量不能绑定 no-const 左值引用类型

error: cannot bind non-const lvalue reference of type ‘T&’ to an rvalue of type ‘T’ 如果一个参数是以非 const 引用传入,c++ 编译器就有理由认为程序员会在函数中修改这个值,并且这个被修改的引用在函数返回后要发挥作用。 但如果你 把一个临时变量当作非 const 引用参数传进来,由于临时变量的特殊性,程序员并不能操作临时变量,而且临时变量随时可能被释放掉。 所以,修改一个临时变量是毫无意义的。据此,c++ 编译器加入了临时变量不能作为非 const 引用的这个语义限制。 c++ 中临时变量是右值类型,不能取引用,只能在当前行使用,不能作为非 const 的引用参数 std::move() 返回的也是一个右值 对于临时变量或字面量的右值引用可以使用 cosnt & 或 && 类型的参数接收 一般来说,右值的地址不能通过解引用来获得,因为它们是字面量,或者因为它们本质上是临时的(例如由函数或显式构造函数调用返回的值)。通过将一个对象传递给这个函数,可以获得一个引用它的右值。 class T{ public: T(int v){ a = v; } T operator+(T &t){ T temp = this->a + t.a; return temp; } void printT(T &t){ cout << "a = " << t....

January 4, 2022 · 1 min · Rick Cui

操作符重载

还有类型转换函数 #include <iostream> using namespace std; class Complex{ // friend Complex complexAdd(Complex &c1, Complex &c2); // friend Complex operator+(Complex &c1, Complex &c2); // friend Complex& operator+= (Complex &c1, Complex &c2); friend ostream& operator<<(ostream &os, Complex &c); friend istream& operator>>(istream &is, Complex &c); public: Complex(){ cout << "Complex()..." << endl; a = 0; b = 0; } Complex(int a, int b){ cout << "Complex(int, int)..." << endl; this->a = a; this->b = b; } ~Complex(){ cout << "~Complex()....

January 3, 2022 · 4 min · Rick Cui

具有链表特性的类

买卖货物问题: #include <iostream> using namespace std; class Goods{ public: Goods(){ m_pNext = nullptr; m_weight = 0; cout << "买入了货物,重量是:" << m_weight << endl; } Goods(int weight){ m_pNext = nullptr; m_weight = weight; total_weight += m_weight; cout << "买入了货物,重量是:" << m_weight << endl; } ~Goods(){ total_weight -= m_weight; // m_pNext 不是在本类中 new 的,所以也不用 delete this->m_pNext = nullptr; cout << "卖出了货物,重量是:" << m_weight << endl; } static int getTotalWeight(){ return total_weight; } Goods *m_pNext; private: int m_weight; static int total_weight; }; int Goods::total_weight = 0; void buy(Goods *&head, int weight){ Goods *pNewGoods = new Goods(weight); if(head == nullptr){ head = pNewGoods; } else{ pNewGoods->m_pNext = head; head = pNewGoods; } } void sale(Goods *&head){ if(head == nullptr){ cout << "没有货物了" << endl; return; } Goods *temp = head; head = head->m_pNext; delete temp; temp = nullptr; } int main( ) { Goods *head = nullptr; int choice; do{ // 提供菜单 cout << "输入 1 进货" << endl; cout << "输入 2 出货" << endl; cout << "输入 0 退出" << endl; cin >> choice; switch(choice){ case 0: // 退出 return 0; case 1: { // 进货 int w = 0; cout << "输入货物重量:" << endl; cin >> w; buy(head, w); break; } case 2: // 出货 sale(head); break; } cout << "目前货物的总重量是:" << Goods::getTotalWeight() << endl; }while(1); return 0; } 输出:...

January 3, 2022 · 2 min · Rick Cui

STL 总结

1、六大组件介绍 容器:数据结构,用来存放数据 算法:常用算法 迭代器:容器和算法之间的胶合剂,“范型指针” 仿函数:一种重载了operator()的类,使得这个类的使用看上去像一个函数 配置器:为容器分配并管理内存 适配器:修改其他组件接口 2、STL 常用的容器有哪些以及各自的特点是什么? 名称 特点 vector 底层数据结构为数组,支持快速随机访问 list 底层数据结构为双向链表,支持快速增删 deque 底层数据结构为一个中央控制器和多个缓冲区,支持首尾(中间不能)快速增删,也支持随机访问 stack 底层一般用deque/list实现,封闭头部即可,不用vector的原因应该是容量大小有限制,扩容耗时 queue 底层一般用deque/list实现,封闭头部即可,不用vector的原因应该是容量大小有限制,扩容耗时 priority_queue 底层数据结构一般为vector为底层容器,堆heap为处理规则来管理底层容器实现 set 底层数据结构为红黑树,有序,不重复 multiset 底层数据结构为红黑树,有序,可重复 map 底层数据结构为红黑树,有序,不重复 multimap 底层数据结构为红黑树,有序,可重复 unordered_set 底层数据结构为hash表,无序,不重复 unordered_multiset 底层数据结构为hash表,无序,可重复 unordered_map 底层数据结构为hash表,无序,不重复 unordered_multimap 底层数据结构为hash表,无序,可重复 3、vector 和 list 的区别 vector底层实现是数组,所以在内存中是连续存放的,随机读取效率高,但插入、删除效率低;list底层实现是双向链表,所以在内存中是任意存放的,插入、删除效率高,但访问元素效率低 vector在中间节点进行插入、删除会导致内存拷贝,而list不会 vector一次性分配好内存,不够时才进行2倍扩容;list每次插入新节点都会进行内存申请 4、vector 扩容原理 以原内存空间大小的两倍配置一份新的内存空间,并将原空间数据拷贝过来进行初始化...

December 30, 2021 · 1 min · Rick Cui