\1. 传递临时变量的问题:
1 2 3 4 5 6 7 8 9 10
| #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
类型的变量中,然后将该变量的引用传递给线程。
1 2 3 4 5 6 7 8 9 10 11
| #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. 传递指针或引用指向局部变量的问题:
1 2 3 4 5 6 7 8 9 10 11
| #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
运算符在堆上分配一个整数变量,并将指针指向该变量。
1 2 3 4 5 6 7 8 9 10 11 12
| #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. 传递指针或引用指向已释放的内存的问题:
1 2 3 4 5 6 7 8 9 10 11 12
| #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
函数以及一个已经被释放的指针所指向的整数值作为参数传递给它解决方案是确保在线程函数执行期间,被传递的对象的生命周期是有效的。例如,在主线程中创建并初始化对象,然后将对象的引用传递给线程。
1 2 3 4 5 6 7 8 9 10 11
| #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. 类成员函数作为入口函数,类对象被提前释放
错误示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| #include <iostream> #include <thread>
class MyClass { public: void func() { std::cout << "Thread " << std::this_thread::get_id() << " started" << std::endl; std::cout << "Thread " << std::this_thread::get_id() << " finished" << std::endl; } };
int main() { MyClass obj; std::thread t(&MyClass::func, &obj); return 0; }
|
上面的代码中,在创建线程之后,obj
对象立即被销毁了,这会导致在线程执行时无法访问 obj
对象,可能会导致程序崩溃或者产生未定义的行为。
为了避免这个问题,可以使用 std::shared_ptr
来管理类对象的生命周期,确保在线程执行期间对象不会被销毁。具体来说,可以在创建线程之前,将类对象的指针封装在一个
std::shared_ptr
对象中,并将其作为参数传递给线程。这样,在线程执行期间,即使类对象的所有者释放了其所有权,std::shared_ptr
仍然会保持对象的生命周期,直到线程结束。
以下是使用 std::shared_ptr 修复上面错误的示例:
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 <memory>
class MyClass { public: void func() { std::cout << "Thread " << std::this_thread::get_id() << " started" << std::endl; 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.入口函数为类的私有成员函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| #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
函数。在创建线程时,需要将类对象的指针作为参数传递给线程。