简体   繁体   English

将 static 成员 function 与 pthread_create() 一起使用有什么影响?

[英]What are the implications of using a static member function with pthread_create()?

I'm helping a student with their homework, which is a basic threading exercise.我正在帮助学生完成作业,这是一项基本的线程练习。 Unfortunately, though they're required to use C++11, they're forbidden from using std::thread .不幸的是,虽然他们被要求使用 C++11,但他们被禁止使用std::thread I don't see the rationale, but it's not my homework.我看不出理由,但这不是我的功课。

Here's the class:这是 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;
}

And here's where pthreads comes in.这就是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);
}

It's a pretty basic exercise, except for how much fluff you have to do to get the old and the new to play nice.这是一个非常基本的练习,除了你必须做多少绒毛才能让旧的和新的玩得很好。 And that's what's got me wondering - does using a static member method as the start_routine argument to pthread_create have any undesirable side effects?这就是让我想知道的 - 使用 static 成员方法作为pthread_createstart_routine参数是否有任何不良副作用? I know static member variables and functions don't "belong" to any objects, but I normally think of static variables as being one-per-class, regardless of the number of objects.我知道 static 成员变量和函数不“属于”任何对象,但我通常认为 static 变量是每个类一个,无论对象的数量如何。 If there's only one copy of the static member function, as well, that seems like you'd be shooting yourself in the foot for parallelization.如果也只有一份 static 成员 function 的副本,那似乎你会为了并行化而自取其辱。

Would it just be better, in this case, to make vaccine_count public and make count_vaccines() a global function?在这种情况下,公开疫苗计数并将vaccine_count count_vaccines()设为全局 function 会更好吗?

Do hit me with whatever detail you can muster;用你能收集到的任何细节来打我; I'm very curious.我很好奇。 =) And, as always, thank you all for your time and effort. =) 而且,一如既往,感谢大家的时间和精力。

except for how much fluff you have to do to get the old and the new to play nice.除了你必须做多少绒毛才能让旧的和新的玩得很好。

Well, in the STL, that's essentially what the std::thread is actually doing.好吧,在 STL 中,这基本上就是std::thread实际在做的事情。 If you create a thread and force it to cause a stack unwinding, and if you look at said stack, you'll see a lot of weird pointer arithmetic happening with this and pthread_create (or CreateThread on Windows).如果您创建一个线程并强制它导致堆栈展开,并且如果您查看所述堆栈,您会看到thispthread_create (或 Windows 上的CreateThread )发生了很多奇怪的指针运算。

That being said, it's not unusual in any way to use a static function of a class that then calls a private member of that class on an object instance, even with the std::thread , it really just depends on what you need those functions to do. That being said, it's not unusual in any way to use a static function of a class that then calls a private member of that class on an object instance, even with the std::thread , it really just depends on what you need those functions去做。

does using a static member method as the start_routine argument to pthread_create have any undesirable side effects?使用 static 成员方法作为pthread_create的 start_routine 参数是否有任何不良副作用?

No. At least not from the perspective of functionality;不。至少从功能的角度来看不是; that is, creating a thread on a static member won't cause any UB or crashes directly just because you are using a static function.也就是说,在 static 成员上创建线程不会直接导致任何 UB 或崩溃,因为您使用的是static function。

You do have to account for the fact that your operating on a static member function, but that's no different from having to account for constructors/destructors or any function of the language itself.您确实必须考虑这样一个事实,即您在 static 成员 function 上进行操作,但这与必须考虑构造函数/析构函数或任何 ZC1C425268E68385D14AB5074C17A 本身的语言没有什么不同。 Since this is a homework assignment, it's likely the professor is trying to teach "how things work" less than "how to use C++11".由于这是一项家庭作业,因此教授很可能试图教授“事物如何工作”而不是“如何使用 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()设为全局 function 会更好吗?

Yes and no.是和不是。 Having vaccine_count as a private member then means that count_vaccines must be a friend or static function, and given that vaccine_count seems like an "important" data point that you wouldn't want a "user of the code" inadvertently setting, it's probably better to keep it private. vaccine_count作为私人成员意味着count_vaccines必须是朋友或 static function,并且鉴于vaccine_count似乎是一个“重要”数据点,您不希望“代码用户”无意中设置,它可能更好保密。

You could add getters and setters, but that might complicate the code unnecessarily.您可以添加 getter 和 setter,但这可能会使代码不必要地复杂化。

You could also just make it a public variable if you trust the users of the code to protect that variable (unlikely), and you could also just make count_vaccines a free/global function, but then you need to have the function after the class declaration.如果您信任代码的用户来保护该变量(不太可能),您也可以将其设为公共变量,并且您也可以将count_vaccines免费/全局 function,但随后您需要在 ZC1C425268E68385D1AB5074C17A94F1444026BB4DEB4C 声明之后有 ZC1C425268E68385D1AB5074C17A94F1444CC1C42B4C 声明. And if the class is a complex class (maybe has templates or some other C++ notion), then it can really complicate the code in how you operate on the class. And if the class is a complex class (maybe has templates or some other C++ notion), then it can really complicate the code in how you operate on the class.

So yes, it could go that way, but the professor is likely trying to teach the idea of what a static function is, how threads operate on the class and how pointers work within the constructs of this exercise, among other things. So yes, it could go that way, but the professor is likely trying to teach the idea of what a static function is, how threads operate on the class and how pointers work within the constructs of this exercise, among other things.

If you have a static member variable, all objects access that variable.如果您有 static 成员变量,则所有对象都访问该变量。

That's not what static means in this context.这不是static在这种情况下的意思。 The static keyword in C++ simply means that you do not need an object reference to call that code. C++ 中的static关键字仅仅意味着您不需要 object 引用来调用该代码。 So a static member variable can be accessed, not just by any object, but by any code, take this example:因此,不仅可以通过任何 object,还可以通过任何代码访问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 成员 function... 可以同时在多个内核上调用吗?

The static keyword has no effect on which core, or thread, said function is called on or if can be done in parallel. static关键字对哪个内核或线程没有影响,如 function 被调用或是否可以并行完成。

A CPU core can only execute 1 machine level instruction per clock cycle (so essentially, just 1 assembly instruction), when a C++ program is compiled, linked and assembled, it is these "assembled" set of instructions base on the syntax you wrote that are executed on the core (or cores) of your CPU, not the static functions.一个 CPU 内核每个时钟周期只能执行 1 条机器级指令(因此基本上只有 1 条汇编指令),当 C++ 程序被编译、链接和汇编时,正是这些“汇编”指令集基于您编写的语法在 CPU 的核心(或多个核心)上执行,而不是static函数。

That static function is just an address in memory that gets called on any number of threads on any CPU core that the OS determines at any given time in your program. static function 只是 memory 中的一个地址,它在操作系统在任何给定时间确定的任何 CPU 内核上的任意数量的线程上被调用。

Yes, you could call an OS API that pins that thread of execution calling that function to a specific core, but that's a different subject.是的,您可以调用操作系统 API 将调用 function 的执行线程固定到特定内核,但这是一个不同的主题。

And for a last little bit of fun for you, on an assembly level, C++ functions basically get compiled into C-like functions (an extreme over simplification, but merely for demonstration):最后,为您带来一点乐趣,在汇编级别上,C++ 函数基本上被编译成类似 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 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;
}

Again, an over simplification, but the point is to illustrate how it all builds to assembly and what the assembly does.再次,过度简化,但重点是说明它是如何构建到装配的以及装配的作用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM