1. 传递临时变量的问题:
#include <iostream>
#include <thread>
void foo(int& x) {
x += 1;
}
int main() {
std::thread t(foo, 1); // 传递临时变量
t.join();
return 0;
}
在这个例子中,我们定义了一个名为`foo`的函数,它接受一个整数引用作为参数,并将该引用加1。然后,我们创建了一个名为`t`的线程,将`foo`函数以及一个临时变量`1`作为参数传递给它。这样会导致在线程函数执行时,临时变量`1`被销毁,从而导致未定义行为。
解决方案是将变量复制到一个持久的对象中,然后将该对象传递给线程。例如,我们可以将`1`复制到一个`int`类型的变量中,然后将该变量的引用传递给线程。
#include <iostream>
#include <thread>
void foo(int& x) {
x += 1;
}
int main() {
int x = 1; // 将变量复制到一个持久的对象中
std::thread t(foo, std::ref(x)); // 将变量的引用传递给线程
t.join();
return 0;
}
2. 传递指针或引用指向局部变量的问题:
#include <iostream>
#include <thread>
void foo(int* ptr) {
std::cout << *ptr << std::endl; // 访问已经被销毁的指针
}
int main() {
int x = 1;
std::thread t(foo, &x); // 传递指向局部变量的指针
t.join();
return 0;
}
在这个例子中,我们定义了一个名为`foo`的函数,它接受一个整型指针作为参数,并输出该指针所指向的整数值。然后,我们创建了一个名为`t`的线程,将`foo`函数以及指向局部变量`x`的指针作为参数传递给它。这样会导致在线程函数执行时,指向局部变量`x`的指针已经被销毁,从而导致未定义行为。
解决方案是将指针或引用指向堆上的变量,或使用`std::shared_ptr`等智能指针来管理对象的生命周期。例如,我们可以使用`new`运算符在堆上分配一个整数变量,并将指针指向该变量。
#include <iostream>
#include <thread>
void foo(int* ptr) {
std::cout << *ptr << std::endl;
delete ptr; // 在使用完指针后,需要手动释放内存
}
int main() {
int* ptr = new int(1); // 在堆上分配一个整数变量
std::thread t(foo, ptr); // 将指针传递给线程
t.join();
return 0;
}
3. 传递指针或引用指向已释放的内存的问题:
#include <iostream>
#include <thread>
void foo(int& x) {
std::cout << x << std::endl; // 访问已经被释放的内存
}
int main() {
int* ptr = new int(1);
std::thread t(foo, *ptr); // 传递已经释放的内存
delete ptr;
t.join();
return 0;
}
在这个例子中,我们定义了一个名为`foo`的函数,它接受一个整数引用作为参数,并输出该引用的值。然后,我们创建了一个名为`t`的线程,将`foo`函数以及一个已经被释放的指针所指向的整数值作为参数传递给它解决方案是确保在线程函数执行期间,被传递的对象的生命周期是有效的。例如,在主线程中创建并初始化对象,然后将对象的引用传递给线程。
#include <iostream>
#include <thread>
void foo(int& x) {
std::cout << x << std::endl;
}
int main() {
int x = 1;
std::thread t(foo, std::ref(x)); // 将变量的引用传递给线程
t.join();
return 0;
}
在这个例子中,我们创建了一个名为`x`的整数变量,并初始化为`1`。然后,我们创建了一个名为`t`的线程,将`foo`函数以及变量`x`的引用作为参数传递给它。这样可以确保在线程函数执行期间,变量`x`的生命周期是有效的。
4. 类成员函数作为入口函数,类对象被提前释放
错误示例:
#include <iostream>
#include <thread>
class MyClass {
public:
void func() {
std::cout << "Thread " << std::this_thread::get_id()
<< " started" << std::endl;
// do some work
std::cout << "Thread " << std::this_thread::get_id()
<< " finished" << std::endl;
}
};
int main() {
MyClass obj;
std::thread t(&MyClass::func, &obj);
// obj 被提前销毁了,会导致未定义的行为
return 0;
}
上面的代码中,在创建线程之后,obj 对象立即被销毁了,这会导致在线程执行时无法访问 obj 对象,可能会导致程序崩溃或者产生未定义的行为。
为了避免这个问题,可以使用 std::shared_ptr 来管理类对象的生命周期,确保在线程执行期间对象不会被销毁。具体来说,可以在创建线程之前,将类对象的指针封装在一个 std::shared_ptr 对象中,并将其作为参数传递给线程。这样,在线程执行期间,即使类对象的所有者释放了其所有权,std::shared_ptr 仍然会保持对象的生命周期,直到线程结束。
以下是使用 std::shared_ptr 修复上面错误的示例:
#include <iostream>
#include <thread>
#include <memory>
class MyClass {
public:
void func() {
std::cout << "Thread " << std::this_thread::get_id()
<< " started" << std::endl;
// do some work
std::cout << "Thread " << std::this_thread::get_id()
<< " finished" << std::endl;
}
};
int main() {
std::shared_ptr<MyClass> obj = std::make_shared<MyClass>();
std::thread t(&MyClass::func, obj);
t.join();
return 0;
}
上面的代码中,使用 std::make_shared 创建了一个 MyClass 类对象,并将其封装在一个 std::shared_ptr 对象中。然后,将 std::shared_ptr 对象作为参数传递给线程。这样,在线程执行期间,即使 obj 对象的所有者释放了其所有权,std::shared_ptr 仍然会保持对象的生命周期,直到线程结束。
5.入口函数为类的私有成员函数
#include <iostream>
#include <thread>
class MyClass {
private:
friend void myThreadFunc(MyClass* obj);
void privateFunc(){
std::cout << "Thread " << std::this_thread::get_id() << " privateFunc" << std::endl;
}
};
void myThreadFunc(MyClass* obj) {
obj->privateFunc();
}
int main() {
MyClass obj;
std::thread thread_1(myThreadFunc, &obj);
thread_1.join();
return 0;
}
上面的代码中,将 myThreadFunc
定义为 MyClass
类的友元函数,并在函数中调用 privateFunc
函数。在创建线程时,需要将类对象的指针作为参数传递给线程。