目录
一、并发、进程、线程的基本概念
1、并发
2、进程
3、线程
二、并发的实现方法
1、多进程
2、多线程
三、C++11新标准库
四、创建线程
1、示范线程运行
2、其他方法创建对象
3、join和detach详细讲解
两个或者更多的任务(独立的活动)同时发生(进行):一个程序同时执行多个独立的任务;
比如:单核CPU实现并发,是通过快速切换。但这是假的并发,人正常感觉不出来在快速切换。多核CPU就可以实现真正的并发,一个内核执行一个内容。
使用并发的原因:主要就是同时可以干多个事,提高性能
一个可以执行的程序,windows为exe文件linux下为可执行文件。进程简单的说就是一个可执行程序运行起来,这就叫创建了一个进程。
用来执行代码的。线程这个东西,可以理解为一条代码的执行通路。
a)没一个进程执行起来,都会创建一个主线程
b)当执行可执行程序时,产生一个进程后,这个主线程就随着这个进程默默启动起来了。
除了主线程之外,可以通过写代码来创建其他线程,其他线程走的是别的道路,甚至去不同的地方。每创建一个新线程,就可以在同一时刻,多干一个不同的事(多走一条不同的代码执行路径)。
多线程就实现了程序的并发。当然线程不是越多越好:
1、每个都需要独立的堆栈大约1M。过多会占用过大。
2、线程之间切换也需要保持当前状态,本身也会消耗资源。
3、切换线程也会消耗程序的运行时间
实现并发的手段:
a)通过多个进程实现并发
b)在单独的进程中,写代码创建除了主线程之外的其他线程来实现并发
可以创建多个可执行文件,他们之间的通信可以通过管道。文件、消息队列实现。
线程:是轻量级的进程。每个进程有自己独立的运行路径,但一个进程中的所有线程共享地址空间(共享内存),全局变量、全局内存、全局引用都可以在线程之间传递,所以多线程开销远远小于多进程
多进程并发和多线程并发可以混合使用,但建议优先考虑多线程技术。
windows:CreateThread(), _beginthread(),_beginthreadexe()创建线程;linux:pthread_create()创建线程;不能跨平台。从C++11新标准,C++语言本身增加对多线程的支持,意味着可移植性(跨平台),这大大减少开发人员的工作量。
程序运行起来,生成一个进程,该进程所属的主线程开始自动运行;当主线程从main()函数返回,则整个进程执行完毕
主线程从main()开始执行,那么我们自己创建的线程,也需要从一个函数开始运行(初始函数),一旦这个函数运行完毕,线程也结束运行
整个进程是否执行完毕的标志是:主线程是否执行完,如果主线程执行完毕了,就代表整个进程执行完毕了,此时如果其他子线程还没有执行完,也会被强行终止。
具体步骤如下:
1、包含头文件
2、需要线程运行的子程序
3、创建thread对象,并将函数传递给对象
#include
#include
using namespace std;
void myprint()
{
cout << "我创建了一个线程" << endl;
cout << "我结束了一个线程" << endl;
cout << "我结束了一个线程1" << endl;
cout << "我结束了一个线程2" << endl;
cout << "我结束了一个线程3" << endl;
}
int main()
{
thread mythread(myprint);
mythread.join();
if (mythread.joinable())
{
cout << "1:joinable=true" << endl;
}
else
{
cout << "1:joinable=false" << endl;
}
mythread.join();
if (mythread.joinable())
{
cout << "2:joinable=true" << endl;
}
else
{
cout << "2:joinable=false" << endl;
}
cout << "hello word!" << endl;
return 0;
}
补充:线程可以调用对象、函数指针、lambda表达式。注意调用对象需要重载括号运算符。
对象的方法。
#include
#include
using namespace std;
class Ta
{
public:
int& a;
Ta(int &m_a):a(m_a) {
cout << "我创建了一个Ta" << endl;
}
void operator ()()
{
cout << "我创建了一个线程" << endl;
cout << "我结束了一个线程" << endl;
cout << "a=" << a << endl;
cout << "a=" << a << endl;
cout << "a=" << a << endl;
}
///* Ta(const Ta& obj)
// {
// */cout << "我拷贝了一个Ta" << endl;
// }
~Ta()
{
cout << "我析构了一个Ta" << endl;
}
};
int main()
{
int a = 2;
Ta ta(a);
thread mythread(ta);
mythread.join();
cout << "hello word!" << endl;
cout << "hello word!1" << endl;
cout << "hello word!2" << endl;
cout << "hello word!3" << endl;
cout << "hello word!4" << endl;
}
lambda创建线程
//main函数中
auto lambdaThread = [] {
cout << "我的线程开始执行了" << endl;
//-------------
//-------------
cout << "我的线程开始执行了" << endl;
};
thread myThread(lambdaThread);
myThread.join();
join:
1、join()函数是一个等待线程完成函数,主线程需要等待子线程运行结束了才可以结束.
2、当使用join()函数时,主调线程阻塞,等待被调线程终止,然后主调线程回收被调线程资源,并继续运行;
detach:
当使用detach()函数时,主调线程继续运行,被调线程驻留后台运行,主调线程无法再取得该被调线程的控制权。当主调线程结束时,由运行时库负责清理与被调线程相关的资源。
值得注意的是:
当我们使用detach()时,如果是通过对象创建线程,在创建对象时引用一个参数。
如果主线程运行完毕,这个参数在对象中为引用。如果对象中需要使用这个参数,就会出现问题,因为主线程已经结束了,创建的参数就会释放。这个对象中引用的参数也无法使用,就会出现问题。
在我上面对象创建时就已经有这个例子了,同学们可以自己测试一下。
int a = 2;
Ta ta(a);
thread mythread(ta);
mythread.detach();
cout << "hello word!" << endl;
一般如果我们不确定前面是否使用join和detach,可以使用joinable判断,如果使用其中一个就返回true反知false。
博主个人建议,能用join就别乱用detach。