C++ 多线程 (线程的基本概念及创建)

文章目录

  • 一、线程的基本概念
    • 1.join和detach
    • 2.主线程main函数抛出异常时,新线程的代码保护
    • 3.get_id()获取线程id和系统可运行的线程数指示器
  • 二、线程创建的3种方式
      • 1.使用函数指针创建进程
      • 2.使用函数对象创建进程
      • 3. 使用lambda函数创建进程
  • 三、线程的参数传递
    • 1.值传递和引用传递
    • 2.线程之间的参数移动以及线程的移动

一、线程的基本概念

C++11中与多线程相关的头文件:

  • :声明与线程相关的类std:thread,包含std:this_thread
  • :声明与互斥量相关的类,包括:std:mutex系列类,std:lock_guard,std:unique_lock以及其他的类型和函数

1.join和detach

  • join: 等待线程结束
  • detach: 让线程在后台独立运行,成为守护进程(deamon process)

一个线程只能被join或detach一次。一旦被detach就不能被join,幸运的是可以在join之前加joinable函数来判断

#include
#include
using namespace std;

void function_1()
{
	cout << "Beauty is only skin-deep" << endl;
}
int main()                    //main函数为主线程
{
	thread t1 (function_1);
	//t1.join();              //主线程等待线程t1结束
	t1.detach();              //t1将会在后台独立运行,即为守护进程(daemon process)
	if (t1.joinable())       //线程一旦detach就不能被join,幸运的是,可以用joinable函数判断防止程序崩溃
		t1.join();            
    cout << "run remaining main thread" << endl;
    return 0;
}

2.主线程main函数抛出异常时,新线程的代码保护

在创建t1线程后,主线程main函数肯定是要执行另外的操作以便从创建新线程中获益。然而一旦 main函数中在线程t1被join之前抛出异常,线程t1将被销毁。此时就需要对main函数中的代码使用异常处理来保护新创建的线程t1.

#include
#include
using namespace std;

void function_1()
{
	cout << "Beauty is only skin-deep" << endl;
}
int main()
{
	std::thread t1(function_1);
	try {                                          //若主线程不加异常处理,那么在for循环抛出异常后线程t1将会在被join之前被销毁
		for (int i = 0; i < 100; ++i)
			cout << "from main" << i << endl;
	}
	catch (...) {
		t1.join();
		throw;
	}
	t1.join();

}

3.get_id()获取线程id和系统可运行的线程数指示器

  • 获取主线程id: this_id::get_id().
  • 获取新建线程idt1.get_id().
int main()
{
	string s = "where there is no trust,ther is no love";
	cout <<"main.id: "<< this_thread::get_id() <<endl;

	thread t1((Fctor()), move(s));
	cout <<"t1.id: "<< t1.get_id() << endl;
	t1.join();
	return 0;
}

显示系统可同时运行的最大线程数:thread::hardware_concurrency().

二、线程创建的3种方式

在C++的应用程序中,都有一个默认的主线程:main函数。在C++11中,可以通过创建std:thread类的对象来创建其他的线程,每个std::thread类的对象都可以与一个线程相关联。可以使用std::thread 对象附加一个回调(Callback),当这个新线程启动时,回调将被执行,回调可以为: 函数指针,函数对象,lambda函数。

新线程在创建之后立即开始启动。

1.使用函数指针创建进程

#include
#include
using namespace std;

void function_1()
{
	cout << "Beauty is only skin-deep" << endl;
}
int main()                    //main函数为主线程
{
	thread t1 (function_1);    //使用函数指针创建线程
	//t1.join();              //主线程等待线程t1结束
	t1.detach();              //t1将会在后台独立运行,即为守护进程(daemon process)
	if (t1.joinable())       //线程一旦detach就不能被join,幸运的是,可以用joinable函数判断防止程序崩溃
		t1.join();            
    cout << "run remaining main thread" << endl;
    return 0;
}

2.使用函数对象创建进程

① 仿函数创建线程:
std::thread t1(Fctor()); // 这种表述会让编译器认为创建一个返回值为thread名为t1的函数,函数参数为指向一个函数的指针(该函数返回值为Fctor类,无参数)

class Fctor
{
public:
	void operator()()
	{
		for (int i = 0; i > -100; i--)
			cout << "from t1: " <<i<< endl;
	}
};


int main()
{
	//std::thread t1(Fctor());     // 这种表述会让编译器认为创建一个返回值为thread名为的函数,函数参数为指向一个函数的指针(该函数返回值为Fctor类,无参数)
	std::thread t1((Fctor()));    //利用仿函数创建线程
	for (int i = 0; i < 5; i++)
		std::cout << "run middle main thread" << std::endl;
	
	try {
		for (int i = 0; i < 100; ++i)
			cout << "from main: " <<i<< endl;
	}
	catch (...) {
		t1.join();
		throw;
	}
	t1.join(0);
	return 0;

}

② 创建类对象绑定线程对象创建线程

int main()
{
	Fctor fct;                    //方法②:创建Fctor线程对象fct并绑定线程t1
	std::thread t1(fct);
	
	for (int i = 0; i < 5; i++)
		std::cout << "run middle main thread" << std::endl;
	
	try {
		for (int i = 0; i < 100; ++i)
			cout << "from main: " <<i<< endl;
	}
	catch (...) {
		t1.join();
		throw;
	}
	t1.join(0);
	return 0;

}

3. 使用lambda函数创建进程

thread t([](int x){ return x*x; }, 6);

三、线程的参数传递

1.值传递和引用传递

线程默认只能进行值传递,即将参数复制到线程的内存空间,而无法进行引用传递

#include
#include
#include
using namespace std;

class Fctor 
{
public:
	void operator()(string msg)
	{
		cout << "t1 says: " << msg << endl;
		msg = "Trust is the mother of deceit.";
	}
};

int main()
{
	string s = "Where there is no trust,there is no love";
	thread t1((Fctor()), s);
	t1.join();
	cout << "from main: " << s << endl;
	return 0;

}

输出:
在这里插入图片描述

#include
#include
#include
using namespace std;

class Fctor 
{
public:
	void operator()(string& msg)
	{
		cout << "t1 says: " << msg << endl;
		msg = "Trust is the mother of deceit.";
	}
};

int main()
{
	string s = "Where there is no trust,there is no love";
	thread t1((Fctor()), s);

	t1.join();
	cout << "from main: " << s << endl;
	return 0;

}

输出:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20190715114514366.png
对线程实现引用传递的方式:
使用 std::ref 显式地声明传入线程的参数为原参数的引用,共享一段内存空间

class Fctor 
{
public:
	void operator()(string& msg)
	{
		cout << "t1 says: " << msg << endl;
		msg = "Trust is the mother of deceit.";
	}
};

int main()
{
	string s = "Where there is no trust,there is no love";
	thread t1((Fctor()), ref(s));     

	t1.join();
	cout << "from main: " << s << endl;
	return 0;

}

在这里插入图片描述

2.线程之间的参数移动以及线程的移动

将参数从主线程移动到新建线程以及线程之间的移动std::move

  • 使用move将主线程参数移动到线程t1,此时主线程main函数的s变为空
  • 使用move将线程t1移动到另一个新建线程t2,线程t1变为空
    (线程不能被复制,thread t2 = t1 非法
#include
#include
#include
using namespace std;

class Fctor
{
public:
	void operator()(string& msg)
	{
		cout << "t1 says: " << msg << endl;
		msg = "Trust is the mother of deceit.";
	}
};

int main()
{
	string s = "Where there is no trust,there is no love";
	thread t1((Fctor()), move(s));    //使用move将主线程参数移动到线程t1,此时主线程main函数的s变为空

	thread t2 = move(t1);             //使用move将线程t1移动到另一个新建线程t2,线程t1变为空
	t2.join();
	cout << "from main: " << s << endl;
	return 0;

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