C++11并发与多线程

目录

一、并发、进程、线程的基本概念

1、并发

2、进程

3、线程

 二、并发的实现方法

1、多进程

2、多线程

三、C++11新标准库

四、创建线程

1、示范线程运行

2、其他方法创建对象

3、join和detach详细讲解


一、并发、进程、线程的基本概念

1、并发

        两个或者更多的任务(独立的活动)同时发生(进行):一个程序同时执行多个独立的任务;

        比如:单核CPU实现并发,是通过快速切换。但这是假的并发,人正常感觉不出来在快速切换。多核CPU就可以实现真正的并发,一个内核执行一个内容。

使用并发的原因:主要就是同时可以干多个事,提高性能

2、进程

        一个可以执行的程序,windows为exe文件linux下为可执行文件。进程简单的说就是一个可执行程序运行起来,这就叫创建了一个进程。

3、线程

        用来执行代码的。线程这个东西,可以理解为一条代码的执行通路。

        a)没一个进程执行起来,都会创建一个主线程

        b)当执行可执行程序时,产生一个进程后,这个主线程就随着这个进程默默启动起来了。

        除了主线程之外,可以通过写代码来创建其他线程,其他线程走的是别的道路,甚至去不同的地方。每创建一个新线程,就可以在同一时刻,多干一个不同的事(多走一条不同的代码执行路径)。

        多线程就实现了程序的并发。当然线程不是越多越好:

1、每个都需要独立的堆栈大约1M。过多会占用过大。

2、线程之间切换也需要保持当前状态,本身也会消耗资源。

3、切换线程也会消耗程序的运行时间

 二、并发的实现方法

实现并发的手段:
a)通过多个进程实现并发
b)在单独的进程中,写代码创建除了主线程之外的其他线程来实现并发

1、多进程

        可以创建多个可执行文件,他们之间的通信可以通过管道。文件、消息队列实现。

2、多线程

线程:是轻量级的进程。每个进程有自己独立的运行路径,但一个进程中的所有线程共享地址空间(共享内存),全局变量、全局内存、全局引用都可以在线程之间传递,所以多线程开销远远小于多进程

多进程并发和多线程并发可以混合使用,但建议优先考虑多线程技术。

三、C++11新标准库

        windows:CreateThread(), _beginthread(),_beginthreadexe()创建线程;linux:pthread_create()创建线程;不能跨平台。从C++11新标准,C++语言本身增加对多线程的支持,意味着可移植性(跨平台),这大大减少开发人员的工作量。

四、创建线程

1、示范线程运行

程序运行起来,生成一个进程,该进程所属的主线程开始自动运行;当主线程从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表达式。注意调用对象需要重载括号运算符。

2、其他方法创建对象

对象的方法。

#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();

3、join和detach详细讲解

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。

你可能感兴趣的:(C++,c++,多线程,并发)