简体   繁体   中英

Several ways to use pthread_create with a member function of a C ++ class: Error when function has input parameter

I am testing different ways of executing as a thread a function defined in a C ++ class using pthread_create.

The program that I attach as an example compile and works correctly in these cases:

  • If the function is static: ThRoutine1
  • If the function is friend: ThRoutine2
  • If the function is neither static nor friend and has no parameters, an intermediate function can be used: runThRoutine3 and ThRoutine3

I compile the program with the command:

g ++ -Wall -g example_program.cpp -lpthread -o example_program

However, I can not create a thread with the ThRoutine4 function that is neither static nor friend and has an input parameter.

After reading several posts on various forums, I have tried to define the s_param structure and the unThRoutine4 function, but when compiling the program I get these errors (it is necessary to uncomment the lines that appear commented):

example_program.cpp: In static member function ‘static void * MyClass :: runThRoutine4 (void *)’:
example_program.cpp: 44: 34: error: ‘class MyClass’ has no member named ‘ptr’
       return ((MyClass *) bundle) -> ptr-> ThRoutine4 (bundle-> number);
                                  ^
example_program.cpp: 44: 56: error: ‘void *’ is not a pointer-to-object type
       return ((MyClass *) bundle) -> ptr-> ThRoutine4 (bundle-> number);

I appreciate any help in this regard.

The source code of example_program.cpp is:

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>

#define _REENTRANT


typedef void* (*THREAD_FUNC_PTR)(void *);

struct s_param
{
  void  *ptr;
  int   number;
};

class MyClass
{
  public:
    static void *ThRoutine1 (int number)
    {
      printf("Hello form ThRoutine1 theard. Number = %d\n", number);
    }

    friend void *ThRoutine2 (int number);

    void *ThRoutine3 ()
    {
      printf("Hello form ThRoutine3 theard.\n");
    }

    static void *runThRoutine3 (void *context)
    {
        return ((MyClass *)context)->ThRoutine3();
    }

    void *ThRoutine4 (int number)
    {
      printf("Hello form ThRoutine4 theard. Number = %d\n", number);
    }

    // It is necessary to uncomment this function to get the indicated errors.
    //
    // static void *runThRoutine4(void *bundle)
    // {
    //  return((MyClass *)bundle)->ptr->ThRoutine4(bundle->number);
    //}

};



void *ThRoutine2 (int number)
{
  printf("Hello form ThRoutine2 theard. Number = %d\n", number);
}


int main(void)
{
  int t = 1;
  pthread_t tid[4]; // an array to keep track of the threads
  MyClass cppClass;

  pthread_create(&tid[1],NULL,(THREAD_FUNC_PTR)&cppClass.ThRoutine1,(int *)t++);

  pthread_create(&tid[2],NULL,(THREAD_FUNC_PTR)&ThRoutine2,(int *)t++);

  pthread_create(&tid[3], NULL, &MyClass::runThRoutine3, &cppClass);

  // It is necessary to uncomment this source code line to get the indicated errors.
  //
  // pthread_create(&tid[4], NULL, &MyClass::runThRoutine4, &cppClass);


  printf("Parent is running.\n");
  sleep(10);

  return 0;
}

I just was about to fix OPs code but then I stopped and restarted:

  1. There is a lot of C code which would be done different in C++.
  2. It feels very wrong to fiddle with the pthread_create() because there is already a nice wrapper with std::thread .

While fiddling with OPs code I noticed a lot of other weaknesses which even should be an issue in C as well like eg functions with a non- void return type but no return statement in body.

So, this is what I got after rewriting OPs code in C++:

#include <iostream>
#include <sstream>
#include <thread>

class MyClass
{
  public:
    static void ThRoutine1 (int number)
    {
      std::ostringstream out;
      out << "Hello from ThRoutine1 thread. Number = " << number << '\n';
      std::cout << out.str();
    }

    friend void ThRoutine2 (int number);

    void ThRoutine3(int number)
    {
      std::ostringstream out;
      out << "Hello from ThRoutine3 thread. Number = " << number << '\n';
      std::cout << out.str();
    }

#if 0 // Oops. It's nearly identical to ThRoutine3() now...
    void ThRoutine4 (int number)
    {
      std::ostringstream out;
      out << "Hello from ThRoutine4 thread. Number = " << number << '\n';
      std::cout << out.str();
    }
#endif // 0

};

void ThRoutine2 (int number)
{
  std::ostringstream out;
  out << "Hello from ThRoutine2 thread. Number = " << number << '\n';
  std::cout << out.str();
}

int main()
{
  int t = 1;
  std::thread threads[3];
  MyClass cppClass;
  threads[0] = std::thread(&MyClass::ThRoutine1, t++);
  threads[1] = std::thread(&ThRoutine2, t++);
  threads[2] = std::thread(&MyClass::ThRoutine3, &cppClass, t++);
  
  std::cout << "Parent is running.\n";
#if 0 // Waiting a while is not a reliable synchronization...
  sleep(10);
#else // ...but join is:
  for (std::thread &thread : threads) {
    if (thread.joinable()) thread.join();
  }
#endif // 0
}

Output:

Parent is running.
Hello from ThRoutine2 thread. Number = 2
Hello from ThRoutine1 thread. Number = 1
Hello from ThRoutine3 thread. Number = 3

Live Demo on coliru

Notes:

  1. I removed MyClass::ThRoutine4() after I realized that this was already covered by MyClass::ThRoutine3() (which I modified slightly to “follow the general pattern”).

  2. I replaced C-ish printf() by C++ stream output. Please, note, that C++ stream output is thread-safe. However, composing output with stream operators ( << ) may cause a mixing of outputs from multiple threads. (I noticed this in my first version.) This could be fixed by introducing a mutex – or even simpler by pre-composing the output in a std::stringstream .

  3. When I was about to replace the sleep() by std::this_thread::sleep() I became aware that OP used this as kind of synchronization. Please, note: Time (or delay) is an unreliable synchronization for threads. So, I replaced this with a thread::join() .

  4. My original intention was to demonstrate as well how nicely lambdas can be used for adapters (to fit a certain function into a required signature). While implementing I realized that the std::thread::thread() is expressive enough to cover all cases of OP without lambda-adapters.


I thought a while whether it's a reasonable answer to solve OPs issue with pthread_create() using std::thread instead. IMHO, it is.

  1. std::thread is the C++ wrapper for pthread s on systems where pthread is supported.

  2. On platforms (eg Windows / Visual Studio) where there is something else than pthread , the std::thread will wrap something else. So, the same code can be compiled without any modification.

    Live Demo on Compiler Explorer

  3. The techniques OP tried to use feel somehow familiar to me. The reason is that I started Object Oriented Programming in C (a long time ago) and switched to C++ later. Thereby, I carried (my) common C techniques into my C++ code and needed a while to switch to more idiomatic C++ code (after realizing that I started to fight against windmills).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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