代码片段管理(自制脚本)
- 工具菜单 ——>代码管理器
- 随便拿一个脚本,自己用记事本打开,修改后另存为一个脚本,再导入到myCode即可
并发、进程、线程的基本概念
- 核心的应用技术(编程通用性技术,不同语言的,接口可能不一样)
数据库编程
网络编程
多线程编程 - 并发概念:两个或者多个独立活动同时进行(官方解释)
【通常做法:先吃饭,再去上厕所,或者先上厕所,再去吃饭】
【蹲在马桶上吃饭】
并发假象:以前的电脑单核采用上下文的形式进行实现的并发(比如:吃饭的几个步骤和上厕所的几个步骤交替进行) - 进程概念:
(1)计算机中的程序关于某一个数据集合上的一次运行活动(exe的运行的状态)
(2)每一个进程都有一个主线程并且只有一个主线程(类似,每个程序只有一个main函数,不能有多个main函数)。
(3)vs编译器中按ctr+F5运行程序本质其实就是主线程调用main函数的代码。 - 线程的概念:
线程其实就是一个代码的运行通道。自己可以创建多个线程(多个执行通道(路径))。
例子:长沙->哈尔滨
长沙->北京->哈尔滨 - 并发实现
(1)多进程实现并发:主要是进程间通信
1、一个电脑上,管道、文件、消息队列、内存共享
2、socket网络通信实现
(2)单个进程,多个线程实现并发(就是一个主线程,多个子线程)
线程多种创建方式
1、需要包含c++多线程库
#include <thread>
2、创建线程:thread类去创建一个线程——需传递一个函数指针
而已
注意:创建线程不做处理,就会调用abort终止程序
。
#include <iostream>
#include <thread>
using namespace std;
// 吃饭子线程
void print()
{
cout<<"吃饭的过程!"<<endl;
}
int main()
{
// 创建线程(一个吃饭的线程):通过普通函数去创建线程
thread test(print); // 传递一个函数指针就可
// 主线程
cout << "散步的过程!...." <<endl;
return 0;
}
// 会报abort()错误!
// 执行过程:散步->吃饭。主线程已经结束,子线程还在继续!引发中断!因为子线程是依附在主线程上的。
3、处理线程
(1)join()
函数:汇合线程,阻塞主线程。(让主线程等待子线程运行完成后,才继续执行主线程)————线程阻塞
。
#include <iostream>
#include <thread>
using namespace std;
// 吃饭子线程
void print()
{
cout<<"吃饭的过程!"<<endl;
}
int main()
{
// 创建线程(一个吃饭的线程):通过普通函数去创建线程
thread test(print); // 传递一个函数指针就可
test.join();
// 主线程
cout << "散步的过程!...." <<endl;
return 0;
}
// 执行过程:吃饭->散步。
(2)detach()
函数:分离,打破依赖关系,把子线程编程 驻留 后台(主线程如果先执行完,已结束,子线程在后台慢慢执行完成,这可能看不到子线程的执行结果)。
#include <iostream>
#include <thread>
using namespace std;
// 吃饭子线程
void print()
{
cout<<"吃饭的过程!"<<endl;
}
int main()
{
// 创建线程(一个吃饭的线程):通过普通函数去创建线程
thread test(print); // 传递一个函数指针就可
test.detach();
// 主线程
cout << "散步的过程!...." <<endl;
return 0;
}
// 执行结果:散步,看不到吃饭(可能)。
加入等待函数Sleep()
#include <iostream>
#include <thread>
#include <windows.h>
using namespace std;
// 吃饭子线程
void print()
{
cout<<"吃饭的过程!"<<endl;
}
int main()
{
// 创建线程(一个吃饭的线程):通过普通函数去创建线程
thread test(print); // 传递一个函数指针就可
test.detach();
Sleep(5000);
// 主线程
cout << "散步的过程!...." <<endl;
return 0;
}
// 执行结果:散步->吃饭。
注意点:一个线程只能被处理一次,如果你join就不能detach,如果你detach就不能join
。
(3)joinable:
判断当前线程是否可以被detach或者join,可以返回true,不可以返回false。
#include <iostream>
#include <thread>
using namespace std;
// 吃饭子线程
void print()
{
cout<<"吃饭的过程!"<<endl;
}
int main()
{
// 创建线程(一个吃饭的线程):通过普通函数去创建线程
thread test(print); // 传递一个函数指针就可
test.detach();
if(test.joinable())
{
test.join();
}
else
{
cout<<"当前线程已经被处理!"<<endl;
}
// 主线程
cout << "散步的过程!...." <<endl;
return 0;
}
// 执行结果:当前线程已经被处理!
// 散步-》吃饭。
其他的几种线程创建方式
1、通过普通函数
创建线程
2、通过类的对象
创建线程
仿函数
:类的对象模仿函数的行为。本质就是重载()运算符
。(就是使一个类的对象使用看上去像一个函数
。其实现就是类中实现一个operator(),这个类就有了类似函数的行为,就是一个仿函数类
了)
#include <iostream>
using namespace std;
class MM
{
public:
MM()
{
cout<<"调用构造函数"<<endl;
}
// 仿函数:operator()为函数名
void operator()()
{
cout<< "子线程启动"<<endl;
}
};
int main()
{
// 仿函数的调用
MM mm;
//(1)调用函数的方式实现仿函数调用
mm.operator()();
//(2)重载
mm(); // 也会调用重载函数
//(3)创建无名对象直接调用仿函数
MM(); // 但是会被解析成调用构造函数
//故而,改为如下:
MM{}(); // 注意!!!首先会调用构造函数,然后调用重载函数
return 0;
}
#include <iostream>
#include <thread>
using namespace std;
class MM
{
public:
MM()
{
cout<<"调用构造函数"<<endl;
}
// 仿函数:operator()为函数名
void operator()()
{
cout<< "子线程启动"<<endl;
}
};
int main()
{
MM mm;
thread test(mm); // 类似thread test(mm())
test.join();
thread test2((MM()));// test2(MM())解析问题 解析为函数调用,怎么处理问题,用()
test2.join();
cout<<"主线程..."<<endl;
return 0;
}
// 结果:调用构造函数->子线程启动->调用构造函数->子线程启动->主线程...
3、Lambda表达式
的方式创建线程
#include <iostream>
#include <thread>
using namespace std;
// 求最大值
int Max(int a, int b)
{
return a>b ? a : b;
}
int main()
{
// Lambda表达式:返回的是一个函数指针(并且当前函数的定义也一并完成)——函数的定义变成一个表达式
// []()mutable throw->返回值类型{函数体}——(捕获的方式,函数参数,是否能够修改(mutable表示能修改),是否存在异常,是否存在返回值(指定返回值类型),函数体)
//[]捕获值:使用外部变量的方式
//[this] 地址指针,一般是类的对象
//[=] 值的方式
//[] 不使用外部的值
//[&] 引用的方式
//[&a,=] 组合的方式
// 非常完整版本的Lambda
int (*pFunc)(int,int) = NULL; // 函数指针
pFunc = [](int a, int b)mutable throw()->int{ return a>b ? a : b; };
// throw():不存在异常
auto pFunction = [](int a, int b)mutable throw()->int{ return a>b ? a : b; };
cout << pFunction(1, 2) << endl;
cout << [](int a, int b)mutable throw()->int{ return a>b ? a : b; }(1, 2) << endl;
cout << [](int a, int b){return a>b ? a : b; }(1, 2) << endl;
thread test([]{cout << "子线程启动" << endl; });
test.join();
cout << "主线程..." << endl;
return 0;
}
// 结果:2
//2
//2
//子线程启动->主线程...
4、带参的方式
创建线程
#include <iostream>
#include <thread>
using namespace std;
// 打印函数——传值的方式,不是传引用的方式
void printData(int num)
{
num++;
cout<<"子线程:"<<num <<"启动"<<endl;
}
// 打印函数——传引用的方式
void printValue(int &num)
{
num++;
cout<<"子线程:"<<num <<"启动"<<endl;
}
// 只能传一个常量进来
void print(int &&a)
{
}
int main()
{
int num=0;
int value=0;
thread test(printData,num);
test.join();
thread test2(printValue,ref(value)); // 不管是&value,还是value都会出错。用ref()
test2.join();
cout<<"主线程结束:"<<num<<"..."<<endl;
cout<<"主线程结束:"<<value<<"..."<<endl;
return 0;
}
// 结果:子线程1启动->子线程1启动->主线程结束:0...->主线程结束:1...
5、带智能指针的方式
创建线程
#include <iostream>
#include <thread>
using namespace std;
void print(unique_ptr<int> ptr)
{
// 获取线程地址
cout<<"子线程:"<<ptr.get()<<endl;
// 获取线程id
cout<<"id"<<this_thread::get_id()<<endl;
}
int main()
{
//1、智能指针:能自动管理申请的内存,不需要手动释放内存,会记录对象的个数
// 获取地址:get()
unique_ptr<int> ptr(new int(100));// 用管理int型的智能指针对象ptr 去管理 一个对象(这个对象无名称,其初始化的值为100)
cout<<"访问智能指针对象中管理对象的地址:"<<ptr.get()<<endl; // 获取了ptr对象中管理对象的地址
cout<<"访问智能指针对象中管理对象的值:"<<*ptr.get()<<endl; // 结果为100
thread test(print,move(ptr)); // 使用ptr会出错,用移动语义move()
test.join();
cout<<"主线程id:"<<this_thread::get_id()<<endl;
cout<<"访问智能指针对象中管理对象的地址:"<<ptr.get()<<endl;
return 0;
}
// 结果:访问智能指针对象中管理对象的地址:....(地址)
// 访问智能指针对象中管理对象的值:100
// 子线程:....(地址)
// id:....(子线程id)
// 主线程id:....(主线程id)
// 访问智能指针对象中管理对象的地址:....(地址:00000000,与第一次结果不一样,因为move将ptr移动到了子线程,而主线程的ptr无效了)
6、通过类的成员函数
创建线程
#include <iostream>
#include <thread>
using namespace std;
class MM
{
public:
void print(int &num)
{
num = 100;
cout << "子线程:" << this_thread::get_id() << endl;
}
};
int main()
{
MM mm;
int num = 1001;
//用类的成员函数成为线程处理函数的时候,一定要先告诉别人是哪个对象的成员函数
thread test(&MM::print, mm, ref(num));// &成员函数的指针,哪个对象——mm对象,参数;注意:多了一个参数——指定对象!!!
test.join();
cout << "主线程:" << this_thread::get_id() << endl;
cout << num << endl;
return 0;
}
// 结果:子线程:id——>主线程:id-->100
资源竞争问题——加锁过程
#include <iostream>
#include <thread>
#include <list>
using namespace std;
/*
资源竞争问题:海王的故事
*/
class Seaking
{
public:
void makeGirFriend()
{
for(int i=0;i<1000;i++)
{
cout<<"喜得一枚女盆友"<<i<<endl;
mm.push_back(i);
}
}
void breakUp()
{
for(int i=0;i<1000;i++)
{
if(!mm.empty())
{
cout<<"失去一枚女盆友"<<i<<endl;
mm.pop_front();
}
else
{
cout<<"都分手了,孤独终老"<<endl;
}
}
}
protected:
list<int> mm;
};
int main()
{
Seaking seaking;
thread test1(&Seaking::makeGirFriend,&seaking);
thread test2(&Seaking::breakUp,&seaking);
test1.join();
test2.join();
cout<<"主线程结束......"<<endl;
}
// 结果: