C++:线程(thread)的创建、调用及销毁
在 C++ 中,线程的管理主要依赖于标准库 std::thread
,自 C++11 起,这一功能被标准化,使得我们能够更加方便地创建、管理和销毁线程。这里我们详细讲解线程的创建、调用和销毁流程。
1. 线程的创建
创建线程通常是为了在单独的线程中执行某个任务。我们可以通过 std::thread
对象来创建一个新的线程。一个线程可以从以下几种类型的可调用对象启动:
- 普通函数
- Lambda 表达式
- 函数对象
- 类的成员函数
1.1 使用普通函数
#include <iostream>
#include <thread>
void printMessage() {
std::cout << "Hello from thread!" << std::endl;
}
int main() {
std::thread myThread(printMessage); // 创建一个线程并启动 printMessage
myThread.join(); // 等待线程完成
return 0;
}
std::thread myThread(printMessage)
创建一个线程对象myThread
,并启动执行printMessage
函数。myThread.join()
等待线程完成。如果没有调用join()
或detach()
,程序在结束时会崩溃。
1.2 使用 Lambda 表达式
#include <iostream>
#include <thread>
int main() {
std::thread myThread([]() {
std::cout << "Hello from lambda!" << std::endl;
});
myThread.join();
return 0;
}
- 这里我们创建了一个线程并使用 lambda 表达式作为线程函数。Lambda 允许我们在局部作用域中定义线程任务。
1.3 使用函数对象
#include <iostream>
#include <thread>
class PrintTask {
public:
void operator()() const {
std::cout << "Hello from function object!" << std::endl;
}
};
int main() {
std::thread myThread(PrintTask()); // 创建线程并启动
myThread.join();
return 0;
}
- 这是通过重载
operator()
来定义一个可调用的对象,该对象可以直接用来创建线程。
1.4 使用类的成员函数
#include <iostream>
#include <thread>
class MyClass {
public:
void memberFunction() {
std::cout << "Hello from member function!" << std::endl;
}
};
int main() {
MyClass obj;
std::thread myThread(&MyClass::memberFunction, &obj); // 需要传递对象指针
myThread.join();
return 0;
}
- 如果是成员函数,则需要传递对象指针。
&MyClass::memberFunction
表示成员函数地址,&obj
是指向MyClass
实例的指针。
2. 线程的调用
-
线程的参数传递:你可以向线程函数传递参数,它们会按照值传递的方式进行复制。为了传递引用,可以使用
std::ref
或std::cref
。
#include <iostream>
#include <thread>
void printNumber(int n) {
std::cout << "Number: " << n << std::endl;
}
int main() {
int value = 42;
std::thread myThread(printNumber, value);
myThread.join(); // 等待线程完成
return 0;
}
- 捕获引用:
void increment(int& n) {
++n;
}
int main() {
int value = 0;
std::thread myThread(increment, std::ref(value));
myThread.join();
std::cout << "Value after increment: " << value << std::endl;
return 0;
}
这里 std::ref
确保 value
以引用的形式传递。
3. 线程的同步
join()
:主线程会等待myThread
结束。join
是同步机制,用于确保线程完成后主线程才会继续。
std::thread myThread(task);
myThread.join();
detach()
:将线程从主线程分离,让它独立运行。独立运行的线程在后台执行,主线程不再等待它完成。需谨慎使用,可能引发访问冲突。
std::thread myThread(task);
myThread.detach();
4. 线程的销毁
- 当
std::thread
对象离开作用域时,如果没有调用join()
或detach()
,程序会触发异常终止。 - 使用
join()
可以让主线程等待子线程完成,从而安全地销毁线程。 - 使用
detach()
可以将线程从std::thread
对象中分离,使其成为独立线程。调用detach()
后,std::thread
对象不再管理该线程。
5. 线程的生命周期管理
- RAII:考虑使用 RAII 类管理线程生命周期,确保在对象析构时
join()
或detach()
线程,从而避免泄漏和不正确的管理。
class ThreadGuard {
public:
explicit ThreadGuard(std::thread& t) : thread(t) {}
~ThreadGuard() {
if (thread.joinable()) {
thread.join();
}
}
private:
std::thread& thread;
};
6. 线程的注意事项
- 避免数据竞争和同步问题:线程共享数据时要小心。可以使用
std::mutex
进行保护,或者使用其他同步机制如std::lock_guard
。 - 避免内存泄漏:如果使用
new thread()
,确保delete
以释放分配的内存。 - 检查
joinable()
状态:调用join()
或detach()
前,可以用joinable()
检查线程状态。
7. 示例总结
#include <iostream>
#include <thread>
void task(int id) {
std::cout << "Thread " << id << " is running." << std::endl;
}
int main() {
std::thread t1(task, 1); // 创建线程并传递参数
std::thread t2(task, 2);
t1.join(); // 等待 t1 完成
t2.join(); // 等待 t2 完成
return 0;
}
8. 线程管理总结
- 使用
std::thread
创建和管理线程。 join()
和detach()
用于控制线程的生命周期。- 避免重复
join()
或detach()
,确保资源管理得当。 - 使用同步机制保护共享数据的访问。
通过这种方式,你可以更灵活地创建、管理和销毁 C++ 线程,确保程序的并发性和资源管理的安全性。