C++多线程编程(1):线程的创建方式
文章首发于我的个人博客:欢迎大佬们来逛逛
文章目录
- 进行与线程
- C++中如何实现多线程
- 创建线程的多种方式
- 无参函数
- lambda表达式
- 常成员函数
- not常成员引用函数
- 智能指针
- 仿函数
- 类的普通成员函数
- 综合测试
进行与线程
多线程是指多个线程并发执行的过程。
进程与线程的关系:
- 进程是一个独立运行的应用程序
- 线程是指进程内独立执行的一个单元,一个进程中可能有多个线程。
C++中如何实现多线程
使用 #include <thread>
头文件,里面定义了很多的线程函数。
其中,使用 thread
创建一个线程.
比如我有两个函数,分别是:
- print1是主线程。
- 下面四个函数分别是子线程。
void print1() {
std::cout << "主线程\n";
}
void printtttt1() {
Sleep(2000);
std::cout << "子线程1\n";
}
void printtttt2() {
Sleep(2000);
std::cout << "子线程2\n";
}
void printtttt3() {
Sleep(2000);
std::cout << "子线程3\n";
}
现在我们想让他们四个同时执行?如何操作。
- 首先来创建线程对象:
thread
用作线程对象类型,然后传递一个函数指针(以模板形式)给到这个对象,则这个对象就是一个线程对象。
我们将这三个子线程分别为三个不同的线程对象。print1为主线程。
std::thread t1(printtttt1);
std::thread t2(printtttt2);
std::thread t3(printtttt3);
创建完成后我们使用 join
来将子线程添加到主线程中:
t1.join();
t2.join();
t3.join();
然后运行,就会发现,三个子线程和主线程同时结束,说明多线程有效。
int main() {
std::thread t1(printtttt1);
std::thread t2(printtttt2);
std::thread t3(printtttt3);
//t.join(); //子线程加入主线程
//detach: 子线程和主线程各自玩个的,等待主线程执行完毕
t1.join();
t2.join();
t3.join();
print1();
return 0;
}
观察到细节:
- 三个子线程都有一个等待两秒的功能,如果不是多线程,则很容易想到单纯的运行这四个函数可能需要6秒多才完成。
- 但是他们四个是同时完成的,即只用了两秒。
- 并且我们没有限制线程之间的执行顺序,因此他们的顺序是任意的。
join
函数就是将子线程加入到主线程,然后和主线程一起执行完毕。
还有个 detach
函数:
- detach: 子线程和主线程各玩各的的,等待主线程执行完毕则停止。
t1.detach();
t2.detach();
t3.detach();
print1();
则会出现什么?
- 程序立刻结束,我管你子线程执行了没有,只要我的主线程结束了,则程序就结束。 因此程序直接执行主线程函数,而不会执行三个子线程。
joinable
:对线程是否可以join和detach操作进行判断。即一个线程只能进行一次join或者detach操作,如果你写了很多的代码,明明已经join过一次了,但是忘记了,因此又join了一次,这时就会 报错
!!!
该函数在可以 join 或 detach 的时候返回true,否则返回false。
因此常见的可以避免错误的方式:
if (t1.joinable()) {
t1.join(); //t1.detach()
}
创建线程的多种方式
thread类型的构造函数是怎样的呢? 它可以构造什么样的线程函数对象呢?
template <class _Fn, class... _Args, .......... >
explicit thread(_Fn&& _Fx, _Args&&... _Ax) {
_Start(_STD forward<_Fn>(_Fx), _STD forward<_Args>(_Ax)...);
}
Fn
:接受一个函数指针。Args
:接受函数的参数。
无参函数
这是最简单的创建线程的方式:
// 1. 传递无参void函数
void print() {
std::cout << "子线程: " << "传递无参void函数\n";
}
void create1() {
std::thread t1(print);
t1.join(); //加入主线程
}
lambda表达式
// 2. 传递lambda表达式
void create2() {
std::thread t1([]() {
std::cout << "lambda表达式\n";
});
if (t1.joinable()) {
t1.join();
}
}
常成员函数
- 普通内置类型:int
- 常引用:const string& ; const int&
- 常指针:const int*
- 都可以直接传递
// 3. 传递有参函数
// 3.1 普通参数
void print2(int num, const std::string& name, const int age, const int& yina, const int* cp) {
std::cout << "num: " << num << " name: " << name << " age: " << age << " yina: "
<< yina << " cp: " << *cp << '\n';
}
void create3() {
int num = 10, age = 20, yina = 999, cpnum = 50;
std::thread t1(print2, num, "你好", age, yina, &cpnum);
if (t1.joinable()) {
t1.join();
}
}
not常成员引用函数
- not常引用: 如果是**
不带const
**的引用类型,则必须使用std::ref
修饰,否则会报错: - not常指针:不会报错。
// 必须加以 ref 修饰;否则就传递const的引用
void print3(int& num) {
std::cout << "引用 num: " << num << '\n';
}
void create4() {
int num = 10;
std::thread t1(print3, std::ref(num));
if (t1.joinable()) {
t1.join();
}
}
//不会报错,const和非const的指针都不会报错,传递地址即可
void print4(int* num) {
std::cout << "指针 num: " << *num << '\n';
}
void create5() {
int num = 10;
std::thread t1(print4, &num);
if (t1.joinable()) {
t1.join();
}
}
智能指针
传递智能指针 unique_ptr 需要加 move
移动,因为unique_ptr只允许存在一份,但是移动后本地将消失。
void print5(std::unique_ptr<int> ptr) {
std::cout << "智能指针: " << *ptr.get() << '\n';
}
void create6() {
std::unique_ptr<int> pointer(new int{ 100 });
std::thread t1(print5, std::move(pointer));
if (t1.joinable()) {
t1.join();
}
//null
std::cout << "移动之后,智能指针: " << pointer.get() << '\n';
}
仿函数
直接传递即可。
class Foo {
public:
Foo() {}
void operator()() {
std::cout << "仿函数\n";
}
};
void create7() {
Foo f = Foo();
//1. 仿函数对象
std::thread t1(f);
if (t1.joinable()) {
t1.join();
}
//2. 匿名函数对象
std::thread t2((Foo()));
if (t2.joinable()) {
t2.join();
}
}
类的普通成员函数
函数指针的形式,先传递类名所对应的**函数地址
,然后再传递类对象
**。
class Aoo {
public:
Aoo() {}
void test() {
std::cout << "普通成员函数\n";
}
};
void create8() {
Aoo a = Aoo();
std::thread t1(&Aoo::test,a);
if (t1.joinable()) {
t1.join();
}
}
综合测试
int main() {
create1();
create2();
create3();
create4();
create5();
create6();
create7();
create8();
return 0;
}