1.线程的建立方式
查找了很多资料,std::thread类最简单,最实用了,两个函数:join(),detach()。前面那个会阻塞主线程,后面那个和主线程并发,头文件 #include <thread>。
使用示例:
void threadFunction() {
std::cout << "Hello from thread!" << std::endl;
}
int main() {
std::thread t(threadFunction); // 创建并启动线程
t.join(); // 等待线程结束
// t.detach(); //剥离线程
std::cout << "Hello from main!" << std::endl;
return 0;
}
2.线程安全主要原因
多个线程对统一资源同时访问造成的资源抢占问题导致线程中断或内存泄漏。
2.1 方案一:加锁
最简单的方式了,适用于绝大多数情况(一般对程序变量保护),头文件 #include <mutex>。示例:
std::mutex mtx;//互斥锁
void printFunction(int x) {
std::lock_guard<std::mutex> guard(mtx); // 自动加锁和解锁
//可以看成一个变量,创建了离开作用域会自动销毁,所以自动解锁了
std::cout << "Value: " << x << std::endl;
}
int main() {
std::thread t1(printFunction, 1);
std::thread t2(printFunction, 2);
t1.join();
t2.join();
return 0;
}
2.2 方案二:使用条件变量
就是使用一个变量(一般使用int或bool)通知各个线程,哪一个可以运行,哪一个不可以运行,注意的是这里的条件变量赋值时也需要保护,因为各个线程都会区访问它。可以搭配std::condition_variable使用,它可以检测一个条件语句或者bool值是否为true,否则就循环等待,头文件是#include <condition_variable>。代码示例:
std::mutex mtx;
std::condition_variable cv;
bool ready = false;
void printFunction(int x) {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, []{ return ready; }); // 等待通知 停止任务的执行
std::cout << "Value: " << x << std::endl;
}
void setReady() {
std::lock_guard<std::mutex> guard(mtx);//加锁保护
ready = true;
cv.notify_all(); // 通知所有等待的线程
}
int main() {
std::thread t1(printFunction, 1);
std::thread t2(printFunction, 2);
std::this_thread::sleep_for(std::chrono::seconds(1));//休眠一秒
setReady();//设置条件变量 让子线程继续与运行
t1.join();
t2.join();//阻塞主线程,等待子线程都运行完毕再退出
return 0;
}
2.3 方案三:使用 std::future 和 std::promise
就如名字一样,它俩是如影随形,互相绑定后自动管理,只要改变std::promise的值,std::future就可以安全的获取,但是注意的是不能直接操作该变量,必须使用其内部函数,头文件#include <future>,代码示例:
int calculate(int x) {
return x * 2;
}
int main() {
std::promise<int> prom;
std::future<int> fut = prom.get_future();//这里绑定
std::thread t([&prom]() {//使用lambda表达式作为任务函数
int result = calculate(10);
prom.set_value(result); // 设置计算结果
});
std::cout << "Result: " << fut.get() << std::endl; // 获取结果
t.join();//等待子线程结束然后结束主线程
return 0;
}
2.4 方案四:使用线程局部储存
就是使用该变量时,线程局部存储允许每个线程有自己独立的变量副本(理解为影分身之术),保证线程安全,声明方式是在声明变量的前面使用thread_local关键字。示例:
thread_local int localVar = 0;//线程局部变量的声明
void threadFunction(int id) {
localVar = id;
std::cout << "Thread " << id << " localVar: " << localVar << std::endl;
}
int main() {
std::thread t1(threadFunction, 1);
std::thread t2(threadFunction, 2);
t1.join();
t2.join();
return 0;
}
3.其他
上面的四个方案基本可以解决一般的线程问题了,还有其他问题的那问题就复杂起来了,如果是线程数量多了难以管理这个问题推荐使用线程池的方式来管理。