使用 void* 正确地将对象传递给 pthread_create()
Correctly passing an object to pthread_create() using void*
我一直在遵循 creating POSIX threads 上的说明,并密切关注他们的示例 Pthread 创建和终止。
根据他们的示例,他们将 long
整数值传递给 pthread_create()
,这导致调用类似于此:
pthread_create(&thread, NULL, do_something, (long*)testVal);
我想要 运行 的代码草图是:
#include <iostream>
#include <pthread.h>
using namespace std;
// Simple example class.
class Range {
public:
int start;
int end;
Range(int start_bound, int end_bound) {
start = start_bound;
end = end_bound;
}
void print() {
cout << "\n(" << start << ", " << end << ')';
}
};
void* do_something(void* testVal) {
std::cout << "Value of thread: " << (long)testVal << '\n';
pthread_exit(NULL);
}
int main() {
pthread_t thread;
Range range = Range(1, 2); // What I really want to pass
long testVal = 42; // Let's test
pthread_create(&thread, NULL, do_something, (void*)testVal);
pthread_exit(NULL);
}
这是在作业的上下文中,老师希望我们在 Linux 中使用 gcc,因此我使用 Windows 子系统 Linux 以及这些编译参数:
gcc program.cpp -o program -lstdc++ -lpthread
运行 上面的程序会输出“线程的值:42”。
我的 C++ 指针经验比我晚了几年,但为了好玩,让我们将数据类型从 long
更改为 int
。这会将语句更改为 int testVal = 42
和 std::cout << "Value of thread: " << (int)testVal << '\n';
。然而,这种情况发生了:
error: cast from ‘void*’ to ‘int’ loses precision (int)testVal
warning: cast to pointer from integer of different size pthread_create(&thread, NULL, do_something, (void*)testVal);
使用整数数据类型不是我想要做的,但我包括这个案例是因为我的猜测是问题从这里复合。
问题
如何将 Range
对象传递给 pthread_create()
?我需要做一些事情,
void* do_something(void* range) {
(Range)range.print();
...
...但是我遇到了一堆类型错误。研究有关传递此值的其他问题并没有带来太多清晰度。返回的主要问题是 从类型 'Range' 到类型 'void*' 的无效转换。我不太了解将 void 指针传递给某个值与将 void 指针传递给已寻址类型之间的区别,无法理解出了什么问题。测试指针引用、取消引用和强制转换的各种组合没有帮助。
void* do_something(void* range) {
(Range)range.print();
...
...but am confronted with a bunch of type errors.
这是因为您的 Range
class 不能从指向 void 的指针转换。
Question How do I pass the Range object to pthread_create()?
通过使用间接寻址。 pthread_create
接受指向任何类型对象的 指针 。这就是 void*
的意思;它可以指向任何类型的对象。传递一个对象的地址:
Range range = Range(1, 2);
Range* range_ptr = ⦥
pthread_create(&thread, nullptr, do_something, range_ptr);
// or shorter
pthread_create(&thread, nullptr, do_something, &range);
可以将void*
静态转换回原来的指针类型,然后通过指针间接访问指向的对象:
void* do_something(void* void_ptr) {
Range* range_ptr = static_cast<Range*>(void_ptr);
range_ptr->print();
请注意,使用间接会给示例带来问题:
至少只要线程使用它,指向的对象就必须处于活动状态。这在您的示例中不起作用,因为该对象是 main
中的一个自动变量,该示例在启动另一个线程后立即终止。一种解决方案是通过加入等待另一个线程结束:
// pthread_exit(NULL); <-- replace this
pthread_join(thread, nullptr);
示例质量不高。它在将整数传递给线程时使用重新解释技巧来避免间接寻址,但不应将 long
重新解释为 void*
并返回,因为不能保证它们的大小相同 - 而且't in 64 bit Windows - 因此标准不保证其往返转换的有效性。该示例应该使用 std::intptr_t
.
P.S。不要在 C++ 中使用 NULL
。
P.P.S。避免在 C++ 中使用 pthreads。更喜欢 std::thread
。
在开始手头的任务之前,您需要解决几个基本问题,即将 Range
的实例传递给新线程。
pthread_exit(NULL);
您将在 pthread_exit
的文档中找到有关 pthread_exit()
如何终止调用线程的说明。值得注意的是,在您的程序中,无论如何都不能保证 do_something
在调用 pthread_exit
之前实际执行。 pthread_create
给你的只是一个模糊的承诺,即创建一个新的执行线程,并将很快开始处理它的业务。无法保证它会在 pthread_create
returns 之前 运行ning 开始,并且您无法保证新线程会在 pthread_exit
终止之前 运行。
那么,假设您以某种方式将指向您的 range
对象的指针传递给您的 do_something
。好吧,当 do_something
尝试做某事时,实际的 range
对象已经消失了,你有一个指向已销毁对象的指针,使用它就变成了未定义的行为。
在你的 main
中,&range
给你一个指向这个对象的指针,通常,你可以转换为 void *
并将它传递给 pthread_create
;它 do_something
你可以将它转换回 Range *
并使用它。但在您修复此未定义的行为之前,这是一个有实际意义的问题。
而不是修复它,您可以考虑使用 std::thread
而不是 POSIX 线程,它提供了一个更自然的、本机 C++ 执行线程实现,解决了大多数这些问题(当正确使用)。
我一直在遵循 creating POSIX threads 上的说明,并密切关注他们的示例 Pthread 创建和终止。
根据他们的示例,他们将 long
整数值传递给 pthread_create()
,这导致调用类似于此:
pthread_create(&thread, NULL, do_something, (long*)testVal);
我想要 运行 的代码草图是:
#include <iostream>
#include <pthread.h>
using namespace std;
// Simple example class.
class Range {
public:
int start;
int end;
Range(int start_bound, int end_bound) {
start = start_bound;
end = end_bound;
}
void print() {
cout << "\n(" << start << ", " << end << ')';
}
};
void* do_something(void* testVal) {
std::cout << "Value of thread: " << (long)testVal << '\n';
pthread_exit(NULL);
}
int main() {
pthread_t thread;
Range range = Range(1, 2); // What I really want to pass
long testVal = 42; // Let's test
pthread_create(&thread, NULL, do_something, (void*)testVal);
pthread_exit(NULL);
}
这是在作业的上下文中,老师希望我们在 Linux 中使用 gcc,因此我使用 Windows 子系统 Linux 以及这些编译参数:
gcc program.cpp -o program -lstdc++ -lpthread
运行 上面的程序会输出“线程的值:42”。
我的 C++ 指针经验比我晚了几年,但为了好玩,让我们将数据类型从 long
更改为 int
。这会将语句更改为 int testVal = 42
和 std::cout << "Value of thread: " << (int)testVal << '\n';
。然而,这种情况发生了:
error: cast from ‘void*’ to ‘int’ loses precision (int)testVal
warning: cast to pointer from integer of different size pthread_create(&thread, NULL, do_something, (void*)testVal);
使用整数数据类型不是我想要做的,但我包括这个案例是因为我的猜测是问题从这里复合。
问题
如何将 Range
对象传递给 pthread_create()
?我需要做一些事情,
void* do_something(void* range) {
(Range)range.print();
...
...但是我遇到了一堆类型错误。研究有关传递此值的其他问题并没有带来太多清晰度。返回的主要问题是 从类型 'Range' 到类型 'void*' 的无效转换。我不太了解将 void 指针传递给某个值与将 void 指针传递给已寻址类型之间的区别,无法理解出了什么问题。测试指针引用、取消引用和强制转换的各种组合没有帮助。
void* do_something(void* range) { (Range)range.print(); ...
...but am confronted with a bunch of type errors.
这是因为您的 Range
class 不能从指向 void 的指针转换。
Question How do I pass the Range object to pthread_create()?
通过使用间接寻址。 pthread_create
接受指向任何类型对象的 指针 。这就是 void*
的意思;它可以指向任何类型的对象。传递一个对象的地址:
Range range = Range(1, 2);
Range* range_ptr = ⦥
pthread_create(&thread, nullptr, do_something, range_ptr);
// or shorter
pthread_create(&thread, nullptr, do_something, &range);
可以将void*
静态转换回原来的指针类型,然后通过指针间接访问指向的对象:
void* do_something(void* void_ptr) {
Range* range_ptr = static_cast<Range*>(void_ptr);
range_ptr->print();
请注意,使用间接会给示例带来问题:
至少只要线程使用它,指向的对象就必须处于活动状态。这在您的示例中不起作用,因为该对象是 main
中的一个自动变量,该示例在启动另一个线程后立即终止。一种解决方案是通过加入等待另一个线程结束:
// pthread_exit(NULL); <-- replace this
pthread_join(thread, nullptr);
示例质量不高。它在将整数传递给线程时使用重新解释技巧来避免间接寻址,但不应将 long
重新解释为 void*
并返回,因为不能保证它们的大小相同 - 而且't in 64 bit Windows - 因此标准不保证其往返转换的有效性。该示例应该使用 std::intptr_t
.
P.S。不要在 C++ 中使用 NULL
。
P.P.S。避免在 C++ 中使用 pthreads。更喜欢 std::thread
。
在开始手头的任务之前,您需要解决几个基本问题,即将 Range
的实例传递给新线程。
pthread_exit(NULL);
您将在 pthread_exit
的文档中找到有关 pthread_exit()
如何终止调用线程的说明。值得注意的是,在您的程序中,无论如何都不能保证 do_something
在调用 pthread_exit
之前实际执行。 pthread_create
给你的只是一个模糊的承诺,即创建一个新的执行线程,并将很快开始处理它的业务。无法保证它会在 pthread_create
returns 之前 运行ning 开始,并且您无法保证新线程会在 pthread_exit
终止之前 运行。
那么,假设您以某种方式将指向您的 range
对象的指针传递给您的 do_something
。好吧,当 do_something
尝试做某事时,实际的 range
对象已经消失了,你有一个指向已销毁对象的指针,使用它就变成了未定义的行为。
在你的 main
中,&range
给你一个指向这个对象的指针,通常,你可以转换为 void *
并将它传递给 pthread_create
;它 do_something
你可以将它转换回 Range *
并使用它。但在您修复此未定义的行为之前,这是一个有实际意义的问题。
而不是修复它,您可以考虑使用 std::thread
而不是 POSIX 线程,它提供了一个更自然的、本机 C++ 执行线程实现,解决了大多数这些问题(当正确使用)。