在 C++11 之前,C++ 没有原生的线程支持,开发者需要依赖于操作系统提供的线程库,不同平台的实现各不相同,这导致了跨平台开发的复杂性。C++11 引入了标准线程库,使线程的创建和管理变得更加统一、易用且跨平台。
一、线程的创建#
在 C++11 中,可以通过 std::thread
类创建线程。std::thread
类提供了几种不同的构造方式,用于灵活地创建线程并执行任务。以下是几种常见的线程创建方法:
1.1 头文件与基本概念#
使用线程需要包含头文件:
线程的基本构造函数如下:
1.2 创建线程的注意事项#
- 不可复制:
std::thread
不支持复制,线程对象之间不能拷贝。
- 任务结束后自动终止:线程的任务函数执行完毕后,线程将自动终止。
- 主线程退出影响子线程:如果主线程退出,所有尚未完成的子线程会被强行终止。因此在实际编程中,通常需要通过
join()
或 detach()
处理子线程的资源。
二、使用不同方式创建线程的示例#
2.1 使用普通函数创建线程#
普通函数是最常见的线程任务类型。在下面的例子中,func
是一个普通函数,接收两个参数并通过 std::thread
创建多个线程来执行它。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
#include <iostream>
#include <thread>
#include <unistd.h>
void func(int bh, const std::string& str) {
for (int ii = 1; ii <= 10; ii++) {
std::cout << "第" << ii << "次表白:亲爱的" << bh << "号," << str << std::endl;
Sleep(1); // 模拟工作负载,休眠1秒
}
}
int main() {
std::thread t1(func, 3, "我是一只傻傻鸟。"); // 创建线程 t1,执行 func
std::thread t2(func, 8, "我有一只小小鸟。"); // 创建线程 t2,执行 func
t1.join(); // 主线程等待 t1 结束
t2.join(); // 主线程等待 t2 结束
return 0;
}
|
2.2 使用 lambda 表达式创建线程#
C++11 提供了 lambda 表达式的支持,简化了函数对象的创建。以下是使用 lambda 表达式创建线程的例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
#include <iostream>
#include <thread>
#include <unistd.h>
int main() {
auto f = [](int bh, const std::string& str) {
for (int ii = 1; ii <= 10; ii++) {
std::cout << "第" << ii << "次表白:亲爱的" << bh << "号," << str << std::endl;
Sleep(1); // 模拟工作负载,休眠1秒
}
};
std::thread t3(f, 3, "我是一只傻傻鸟。"); // 使用 lambda 表达式创建线程
t3.join(); // 主线程等待 t3 结束
return 0;
}
|
2.3 使用仿函数(Functor)创建线程#
仿函数是通过重载 operator()
实现的类,可以像普通函数一样调用。以下是通过仿函数创建线程的例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
#include <iostream>
#include <thread>
#include <unistd.h>
class MyThread {
public:
void operator()(int bh, const std::string& str) {
for (int ii = 1; ii <= 10; ii++) {
std::cout << "第" << ii << "次表白:亲爱的" << bh << "号," << str << std::endl;
Sleep(1); // 模拟工作负载,休眠1秒
}
}
};
int main() {
std::thread t4(MyThread(), 3, "我是一只傻傻鸟。"); // 创建线程并使用仿函数执行任务
t4.join(); // 主线程等待 t4 结束
return 0;
}
|
2.4 使用类的静态成员函数创建线程#
静态成员函数不依赖于具体的对象,因此可以直接使用类名调用。以下是通过静态成员函数创建线程的例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
#include <iostream>
#include <thread>
#include <unistd.h>
class MyThread {
public:
static void func(int bh, const std::string& str) {
for (int ii = 1; ii <= 10; ii++) {
std::cout << "第" << ii << "次表白:亲爱的" << bh << "号," << str << std::endl;
Sleep(1); // 模拟工作负载,休眠1秒
}
}
};
int main() {
std::thread t5(MyThread::func, 3, "我是一只傻傻鸟。"); // 使用静态成员函数创建线程
t5.join(); // 主线程等待 t5 结束
return 0;
}
|
2.5 使用类的非静态成员函数创建线程#
非静态成员函数与特定的对象实例关联,调用时需要传递对象指针。以下是通过非静态成员函数创建线程的例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
#include <iostream>
#include <thread>
#include <unistd.h>
class MyThread {
public:
void func(int bh, const std::string& str) {
for (int ii = 1; ii <= 10; ii++) {
std::cout << "第" << ii << "次表白:亲爱的" << bh << "号," << str << std::endl;
Sleep(1); // 模拟工作负载,休眠1秒
}
}
};
int main() {
MyThread obj; // 创建类的对象
std::thread t6(&MyThread::func, &obj, 3, "我是一只傻傻鸟。"); // 创建线程,并绑定对象成员函数
t6.join(); // 主线程等待 t6 结束
return 0;
}
|
三、总结#
C++11 通过 std::thread
类为多线程编程提供了跨平台、统一的接口。开发者可以使用普通函数、lambda 表达式、仿函数、类的静态和非静态成员函数来创建线程。每种方式都有其使用场景和优势,开发者应根据实际需求选择合适的线程创建方法。
注意事项:
std::thread
不支持复制,只能通过移动操作来转移资源。
- 在使用非静态成员函数时,需要传递类对象的指针,确保对象的生命周期比线程长。
join()
用于阻塞主线程,等待子线程结束;detach()
可以使子线程与主线程分离,独立运行。