使用带有 pthread_create() 的静态成员函数有什么含义?
What are the implications of using a static member function with pthread_create()?
我正在帮助学生做家庭作业,这是一项基本的线程练习。不幸的是,虽然他们被要求使用 C++11,但他们被禁止使用 std::thread
。我看不出原理,但这不是我的功课。
这是class:
class VaccineInfo {
public:
VaccineInfo(const std::string &t_input_filename):
input_filename(t_input_filename)
{ }
VaccineInfo() = delete;
static void *count_vaccines(void *t_vi);
int v1_count() { return vaccine_count["v1"]; }
int v2_count() { return vaccine_count["v2"]; }
int v3_count() { return vaccine_count["v3"]; }
private:
std::string input_filename;
std::map<std::string, int> vaccine_count {
{ "v1", 0 },
{ "v2", 0 },
{ "v3", 0 }
};
};
void *VaccineInfo::count_vaccines(void *t_vi) {
VaccineInfo *vi = reinterpret_cast<VaccineInfo*>(t_vi);
std::ifstream input_file;
std::string input_line;
input_file.open(vi->input_filename);
if (!input_file.good()) {
std::cerr << "No such file " << vi->input_filename << std::endl;
return nullptr;
}
while (std::getline(input_file, input_line)) {
vi->vaccine_count[input_line]++;
}
return nullptr;
}
这就是 pthreads
的用武之地。
std::vector<std::string> filenames = find_filenames(".");
std::vector<pthread_t> thread_handles;
std::vector<VaccineInfo> vi_vector;
vi_vector.reserve(filenames.size());
for(const std::string &filename : filenames) {
pthread_t tid;
thread_handles.push_back(tid);
vi_vector.emplace_back(VaccineInfo(filename));
pthread_create(
&thread_handles.back(), nullptr, &VaccineInfo::count_vaccines,
static_cast<void*>(&vi_vector.back()));
}
for (const pthread_t tid : thread_handles) {
pthread_join(tid, nullptr);
}
这是一个非常基本的练习,除了你必须做多少次才能让旧的和新的玩得很好。这就是让我想知道的 - 使用静态成员方法作为 pthread_create
的 start_routine
参数是否有任何不良副作用?我知道静态成员变量和函数不“属于”任何对象,但我通常认为静态变量是一个 class,而不管对象的数量。如果静态成员函数也只有一个副本,那么并行化似乎是搬起石头砸自己的脚。
在这种情况下,将 vaccine_count
public 和 count_vaccines()
设为全局函数会更好吗?
请用你能收集到的任何细节来打击我;我很好奇。 =) 还有,一如既往,感谢大家的时间和努力。
except for how much fluff you have to do to get the old and the new to play nice.
好吧,在 STL 中,这基本上就是 std::thread
的实际作用。如果您创建一个线程并强制它导致堆栈展开,并且如果您查看所述堆栈,您会看到 this
和 pthread_create
(或 CreateThread
在 Windows).
也就是说,使用 class 的 static
函数然后调用 class 的 private
成员在对象实例,即使使用 std::thread
,它实际上只取决于您需要这些函数做什么。
does using a static member method as the start_routine argument to pthread_create
have any undesirable side effects?
没有。至少从功能的角度来看不是;也就是说,在静态成员上创建线程不会导致任何 UB 或直接崩溃,因为您正在使用 static
函数。
您确实必须考虑到您在静态成员函数上进行操作这一事实,但这与必须考虑 constructors/destructors 或语言本身的任何函数没有什么不同。由于这是一项家庭作业,教授可能试图教授“事物如何工作”而不是“如何使用 C++11”。
Would it just be better, in this case, to make vaccine_count
public and make count_vaccines()
a global function?
是也不是。将 vaccine_count
作为私有成员意味着 count_vaccines
必须是友元函数或静态函数,并且考虑到 vaccine_count
似乎是一个“重要”数据点,您不希望“代码的用户”无意中设置,最好将其保密。
您可以添加 getter 和 setter,但这可能会使代码不必要地复杂化。
如果您相信代码的用户会保护该变量(不太可能),您也可以将其设为 public 变量,并且您也可以将 count_vaccines
设为 free/global 函数,但是你需要在 class 声明之后有这个函数。如果 class 是一个复杂的 class(可能有模板或其他一些 C++ 概念),那么它确实会使您在 class.[=38= 上操作的代码复杂化]
所以是的,它可以那样做,但是教授可能会尝试教授什么是静态函数的概念,线程如何在 class 上运行以及指针如何在此构造中工作运动等等。
If you have a static member variable, all objects access that variable.
这不是 static
在这种情况下的意思。 C++ 中的 static
关键字只是意味着您不需要对象引用来调用该代码。所以一个 static
成员变量不仅可以被任何对象访问,而且可以被任何代码访问,举个例子:
class Data {
public:
static int x_val;
int y_val;
};
int Data::x_val; // have to declare it since it's static
int main(int argc, char* argv[]) {
Data::x_val = 10; // works because it's static.
Data::y_val = 10; // error: accessing a non-static member
Data obj;
obj.y_val = 10; // ok because it's a member variable
obj.x_val = 20; // this works as the complier ultimately translates this to `Data::x_val = 20`
// BUT, referencing a static member/function on an object instance is "bad form"
return 0;
}
If you have a static member function... can it be called on more than one core simultaneously?
static
关键字对调用该函数的核心或线程没有影响,或者是否可以并行完成。
一个CPU核心每个时钟周期只能执行1条机器级指令(所以本质上,只是1条汇编指令),当一个C++程序被编译、链接和汇编时,就是这些“汇编”集基于您编写的语法的指令在 CPU 的核心(或多个核心)上执行,而不是 static
函数。
该静态函数只是内存中的一个地址,可以在任何 CPU 核心上的任意数量的线程上调用该地址,OS 在您的程序中的任何给定时间确定。
是的,您可以调用 OS API 将调用该函数的执行线程固定到特定核心,但这是一个不同的主题。
为了给您带来最后一点乐趣,在汇编级别,C++ 函数基本上被编译成类 C 函数(极端过度简化,但仅用于演示):
C++
class Data {
public:
void increment() {
this->y_val += 1024;
}
private:
int y_val;
};
int main() {
Data obj;
obj.y_val = 42;
obj.increment(); // obj.y_val == 1066
return 0;
}
C
struct Data {
int y_val;
};
void Data_increment(Data* this) {
this->y_val += 1024;
}
int main() {
Data obj;
obj.y_val = 42;
increment(&obj); // obj.y_val == 1066
return 0;
}
同样,过于简单化了,但重点是要说明它是如何构建到程序集以及程序集的作用的。
我正在帮助学生做家庭作业,这是一项基本的线程练习。不幸的是,虽然他们被要求使用 C++11,但他们被禁止使用 std::thread
。我看不出原理,但这不是我的功课。
这是class:
class VaccineInfo {
public:
VaccineInfo(const std::string &t_input_filename):
input_filename(t_input_filename)
{ }
VaccineInfo() = delete;
static void *count_vaccines(void *t_vi);
int v1_count() { return vaccine_count["v1"]; }
int v2_count() { return vaccine_count["v2"]; }
int v3_count() { return vaccine_count["v3"]; }
private:
std::string input_filename;
std::map<std::string, int> vaccine_count {
{ "v1", 0 },
{ "v2", 0 },
{ "v3", 0 }
};
};
void *VaccineInfo::count_vaccines(void *t_vi) {
VaccineInfo *vi = reinterpret_cast<VaccineInfo*>(t_vi);
std::ifstream input_file;
std::string input_line;
input_file.open(vi->input_filename);
if (!input_file.good()) {
std::cerr << "No such file " << vi->input_filename << std::endl;
return nullptr;
}
while (std::getline(input_file, input_line)) {
vi->vaccine_count[input_line]++;
}
return nullptr;
}
这就是 pthreads
的用武之地。
std::vector<std::string> filenames = find_filenames(".");
std::vector<pthread_t> thread_handles;
std::vector<VaccineInfo> vi_vector;
vi_vector.reserve(filenames.size());
for(const std::string &filename : filenames) {
pthread_t tid;
thread_handles.push_back(tid);
vi_vector.emplace_back(VaccineInfo(filename));
pthread_create(
&thread_handles.back(), nullptr, &VaccineInfo::count_vaccines,
static_cast<void*>(&vi_vector.back()));
}
for (const pthread_t tid : thread_handles) {
pthread_join(tid, nullptr);
}
这是一个非常基本的练习,除了你必须做多少次才能让旧的和新的玩得很好。这就是让我想知道的 - 使用静态成员方法作为 pthread_create
的 start_routine
参数是否有任何不良副作用?我知道静态成员变量和函数不“属于”任何对象,但我通常认为静态变量是一个 class,而不管对象的数量。如果静态成员函数也只有一个副本,那么并行化似乎是搬起石头砸自己的脚。
在这种情况下,将 vaccine_count
public 和 count_vaccines()
设为全局函数会更好吗?
请用你能收集到的任何细节来打击我;我很好奇。 =) 还有,一如既往,感谢大家的时间和努力。
except for how much fluff you have to do to get the old and the new to play nice.
好吧,在 STL 中,这基本上就是 std::thread
的实际作用。如果您创建一个线程并强制它导致堆栈展开,并且如果您查看所述堆栈,您会看到 this
和 pthread_create
(或 CreateThread
在 Windows).
也就是说,使用 class 的 static
函数然后调用 class 的 private
成员在对象实例,即使使用 std::thread
,它实际上只取决于您需要这些函数做什么。
does using a static member method as the start_routine argument to
pthread_create
have any undesirable side effects?
没有。至少从功能的角度来看不是;也就是说,在静态成员上创建线程不会导致任何 UB 或直接崩溃,因为您正在使用 static
函数。
您确实必须考虑到您在静态成员函数上进行操作这一事实,但这与必须考虑 constructors/destructors 或语言本身的任何函数没有什么不同。由于这是一项家庭作业,教授可能试图教授“事物如何工作”而不是“如何使用 C++11”。
Would it just be better, in this case, to make
vaccine_count
public and makecount_vaccines()
a global function?
是也不是。将 vaccine_count
作为私有成员意味着 count_vaccines
必须是友元函数或静态函数,并且考虑到 vaccine_count
似乎是一个“重要”数据点,您不希望“代码的用户”无意中设置,最好将其保密。
您可以添加 getter 和 setter,但这可能会使代码不必要地复杂化。
如果您相信代码的用户会保护该变量(不太可能),您也可以将其设为 public 变量,并且您也可以将 count_vaccines
设为 free/global 函数,但是你需要在 class 声明之后有这个函数。如果 class 是一个复杂的 class(可能有模板或其他一些 C++ 概念),那么它确实会使您在 class.[=38= 上操作的代码复杂化]
所以是的,它可以那样做,但是教授可能会尝试教授什么是静态函数的概念,线程如何在 class 上运行以及指针如何在此构造中工作运动等等。
If you have a static member variable, all objects access that variable.
这不是 static
在这种情况下的意思。 C++ 中的 static
关键字只是意味着您不需要对象引用来调用该代码。所以一个 static
成员变量不仅可以被任何对象访问,而且可以被任何代码访问,举个例子:
class Data {
public:
static int x_val;
int y_val;
};
int Data::x_val; // have to declare it since it's static
int main(int argc, char* argv[]) {
Data::x_val = 10; // works because it's static.
Data::y_val = 10; // error: accessing a non-static member
Data obj;
obj.y_val = 10; // ok because it's a member variable
obj.x_val = 20; // this works as the complier ultimately translates this to `Data::x_val = 20`
// BUT, referencing a static member/function on an object instance is "bad form"
return 0;
}
If you have a static member function... can it be called on more than one core simultaneously?
static
关键字对调用该函数的核心或线程没有影响,或者是否可以并行完成。
一个CPU核心每个时钟周期只能执行1条机器级指令(所以本质上,只是1条汇编指令),当一个C++程序被编译、链接和汇编时,就是这些“汇编”集基于您编写的语法的指令在 CPU 的核心(或多个核心)上执行,而不是 static
函数。
该静态函数只是内存中的一个地址,可以在任何 CPU 核心上的任意数量的线程上调用该地址,OS 在您的程序中的任何给定时间确定。
是的,您可以调用 OS API 将调用该函数的执行线程固定到特定核心,但这是一个不同的主题。
为了给您带来最后一点乐趣,在汇编级别,C++ 函数基本上被编译成类 C 函数(极端过度简化,但仅用于演示):
C++
class Data {
public:
void increment() {
this->y_val += 1024;
}
private:
int y_val;
};
int main() {
Data obj;
obj.y_val = 42;
obj.increment(); // obj.y_val == 1066
return 0;
}
C
struct Data {
int y_val;
};
void Data_increment(Data* this) {
this->y_val += 1024;
}
int main() {
Data obj;
obj.y_val = 42;
increment(&obj); // obj.y_val == 1066
return 0;
}
同样,过于简单化了,但重点是要说明它是如何构建到程序集以及程序集的作用的。