función pthread de una clase

86

Digamos que tengo una clase como

class c { 
    // ...
    void *print(void *){ cout << "Hello"; }
}

Y luego tengo un vector de c

vector<c> classes; pthread_t t1;
classes.push_back(c());
classes.push_back(c());

Ahora, quiero crear un hilo en c.print();

Y lo siguiente me está dando el siguiente problema: pthread_create(&t1, NULL, &c[0].print, NULL);

Error Ouput: no se puede convertir 'void * (tree_item ::) (void )' a 'void * ( ) (void )' para el argumento '3' a 'int pthread_create (pthread_t *, const pthread_attr_t *, void * ( ) (void ), nulo *) '

Ángel.King.47
fuente

Respuestas:

147

No puede hacerlo de la forma en que lo ha escrito porque las funciones miembro de la clase C ++ tienen un thisparámetro oculto pasado. No pthread_create()tiene idea de qué valor thisusar, por lo que si intenta evitar el compilador al convertir el método en una función puntero del tipo apropiado, obtendrá un error de segmentación. Tienes que usar un método de clase estática (que no tiene thisparámetro), o una función ordinaria simple para arrancar la clase:

class C
{
public:
    void *hello(void)
    {
        std::cout << "Hello, world!" << std::endl;
        return 0;
    }

    static void *hello_helper(void *context)
    {
        return ((C *)context)->hello();
    }
};
...
C c;
pthread_t t;
pthread_create(&t, NULL, &C::hello_helper, &c);
Adam Rosenfield
fuente
¿Funcionaría lo anterior con vectores de la siguiente manera: pthread_create (& t, NULL, & C :: hello_helper, & vector_c [0]); ?
Angel.King.47 de
Todos los comentarios anteriores son útiles. Usé una combinación de todos para resolver un problema. Aún así, no es tan simple como estaba tratando de hacerlo ... Pero desafortunadamente solo puedo marcar uno como el correcto. De lo contrario, todos obtienen el crédito
Angel.King.47
Quería votar a favor de esta respuesta, pero usa moldes de estilo C, que deben terminarse con un prejuicio extremo. Por lo demás, esta respuesta es correcta.
Chris Jester-Young
@Chris: No quiero entrar en una guerra santa sobre los estilos de reparto, pero es perfectamente semánticamente correcto usar un reparto de estilo C en este caso.
Adam Rosenfield
2
@AdamRosenfield también es perfectamente semánticamente correcto encadenar adverbios, ¡pero eso no lo convierte en un buen estilo! xD
ACK_stoverflow
82

Mi forma favorita de manejar un hilo es encapsularlo dentro de un objeto C ++. He aquí un ejemplo:

class MyThreadClass
{
public:
   MyThreadClass() {/* empty */}
   virtual ~MyThreadClass() {/* empty */}

   /** Returns true if the thread was successfully started, false if there was an error starting the thread */
   bool StartInternalThread()
   {
      return (pthread_create(&_thread, NULL, InternalThreadEntryFunc, this) == 0);
   }

   /** Will not return until the internal thread has exited. */
   void WaitForInternalThreadToExit()
   {
      (void) pthread_join(_thread, NULL);
   }

protected:
   /** Implement this method in your subclass with the code you want your thread to run. */
   virtual void InternalThreadEntry() = 0;

private:
   static void * InternalThreadEntryFunc(void * This) {((MyThreadClass *)This)->InternalThreadEntry(); return NULL;}

   pthread_t _thread;
};

Para usarlo, simplemente crearía una subclase de MyThreadClass con el método InternalThreadEntry () implementado para contener el ciclo de eventos de su hilo. Debería llamar a WaitForInternalThreadToSalir () en el objeto hilo antes de eliminar el objeto hilo, por supuesto (y tener algún mecanismo para asegurarse de que el hilo realmente salga, de lo contrario WaitForInternalThreadToSalir () nunca volvería)

Jeremy Friesner
fuente
1
Esa es una gran manera en que puedo entender el uso de la Clase Virtual anterior, pero tengo muchos problemas más graves ... Tengo subprocesos que generan otros subprocesos que deben colocarse todos en un vector. Y luego un bucle recursivo para ir y unir todos los hilos. Estoy seguro de que podría implementar lo anterior para hacer eso también llamando a la espera en el lugar correcto, pero lo intentaré para ver a dónde llego
Angel.King.47 de
4
Esta solución es muy elegante. Lo usaré de ahora en adelante. Gracias Jeremy Friesner. +1
Armada
hola Jeremy Friesner, ¿cómo pasar una referencia a InternalThreadEntry (aclass_ref & refobj)? que cambios debo hacer?
sree
@sree Agrega la referencia (o un puntero) a MyThreadClass como una variable miembro; entonces InternalThreadEntry () puede acceder a él directamente, sin tener que preocuparse por pasarlo a través del argumento (void *).
Jeremy Friesner
10

Deberá asignar pthread_createuna función que coincida con la firma que está buscando. Lo que estás pasando no funcionará.

Puede implementar cualquier función estática que desee para hacer esto, y puede hacer referencia a una instancia cy ejecutar lo que desee en el hilo. pthread_createestá diseñado para tomar no solo un puntero de función, sino también un puntero al "contexto". En este caso, simplemente le pasa un puntero a una instancia de c.

Por ejemplo:

static void* execute_print(void* ctx) {
    c* cptr = (c*)ctx;
    cptr->print();
    return NULL;
}


void func() {

    ...

    pthread_create(&t1, NULL, execute_print, &c[0]);

    ...
}
Jared Oberhaus
fuente
1
ooo veo lo que quieres decir ...
pásale
2

Las respuestas anteriores son buenas, pero en mi caso, el primer enfoque que convierte la función en estática no funcionó. Estaba tratando de convertir el código existente para pasar a la función de subproceso, pero ese código ya tenía muchas referencias a miembros de clase no estáticos. La segunda solución de encapsular en un objeto C ++ funciona, pero tiene envoltorios de 3 niveles para ejecutar un hilo.

Tenía una solución alternativa que usa la construcción C ++ existente - función 'amigo', y funcionó perfectamente para mi caso. Un ejemplo de cómo usé 'amigo' (usaré el mismo ejemplo anterior para los nombres que muestran cómo se puede convertir en una forma compacta usando amigo)

    class MyThreadClass
    {
    public:
       MyThreadClass() {/* empty */}
       virtual ~MyThreadClass() {/* empty */}

       bool Init()
       {
          return (pthread_create(&_thread, NULL, &ThreadEntryFunc, this) == 0);
       }

       /** Will not return until the internal thread has exited. */
       void WaitForThreadToExit()
       {
          (void) pthread_join(_thread, NULL);
       }

    private:
       //our friend function that runs the thread task
       friend void* ThreadEntryFunc(void *);

       pthread_t _thread;
    };

    //friend is defined outside of class and without any qualifiers
    void* ThreadEntryFunc(void *obj_param) {
    MyThreadClass *thr  = ((MyThreadClass *)obj_param); 

    //access all the members using thr->

    return NULL;
    }

Por supuesto, podemos usar boost :: thread y evitar todo esto, pero estaba tratando de modificar el código C ++ para no usar boost (el código se vinculaba con boost solo para este propósito)

sanmara
fuente
1

Mi primera respuesta con la esperanza de que sea útil para alguien: ahora, esta es una pregunta antigua, pero encontré exactamente el mismo error que la pregunta anterior mientras escribía una clase TcpServer y estaba tratando de usar pthreads. Encontré esta pregunta y ahora entiendo por qué estaba sucediendo. Terminé haciendo esto:

#include <thread>

método para ejecutar roscado -> void* TcpServer::sockethandler(void* lp) {/*code here*/}

y lo llamo con una lambda -> std::thread( [=] { sockethandler((void*)csock); } ).detach();

eso me parece un enfoque limpio.

ZoOl007
fuente
0

Demasiadas veces he encontrado formas de solucionar lo que me pides que, en mi opinión son demasiado complicadas. Por ejemplo, tienes que definir nuevos tipos de clases, biblioteca de enlaces, etc. Así que decidí escribir algunas líneas de código que permitan al usuario final básicamente poder "crear hilos" en un "método void :: (void)" de cualquier clase. Seguro que esta solución que implementé se puede ampliar, mejorar, etc., así que, si necesita métodos o funciones más específicos, agréguelos y sea tan amable de mantenerme informado.

Aquí hay 3 archivos que muestran lo que hice.

    // A basic mutex class, I called this file Mutex.h
#ifndef MUTEXCONDITION_H_
#define MUTEXCONDITION_H_

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

class MutexCondition
{
private:
    bool init() {
        //printf("MutexCondition::init called\n");
        pthread_mutex_init(&m_mut, NULL);
        pthread_cond_init(&m_con, NULL);
        return true;
    }

    bool destroy() {
        pthread_mutex_destroy(&m_mut);
        pthread_cond_destroy(&m_con);
        return true;
    }

public:
    pthread_mutex_t m_mut;
    pthread_cond_t m_con;

    MutexCondition() {
        init();
    }
    virtual ~MutexCondition() {
        destroy();
    }

    bool lock() {
        pthread_mutex_lock(&m_mut);
        return true;
    }

    bool unlock() {
        pthread_mutex_unlock(&m_mut);
        return true;
    }

    bool wait() {
        lock();
        pthread_cond_wait(&m_con, &m_mut);
        unlock();
        return true;
    }

    bool signal() {
        pthread_cond_signal(&m_con);
        return true;
    }
};
#endif
// End of Mutex.h

// La clase que encapsula todo el trabajo para procesar un método (test.h):

#ifndef __THREAD_HANDLER___
#define __THREAD_HANDLER___

#include <pthread.h>
#include <vector>
#include <iostream>
#include "Mutex.h"

using namespace std;

template <class T> 
class CThreadInfo
{
  public:
    typedef void (T::*MHT_PTR) (void);
    vector<MHT_PTR> _threaded_methods;
    vector<bool> _status_flags;
    T *_data;
    MutexCondition _mutex;
    int _idx;
    bool _status;

    CThreadInfo(T* p1):_data(p1), _idx(0) {}
    void setThreadedMethods(vector<MHT_PTR> & pThreadedMethods)
    {
        _threaded_methods = pThreadedMethods;
      _status_flags.resize(_threaded_methods.size(), false);
    }
};

template <class T> 
class CSThread {
  protected:
    typedef void (T::*MHT_PTR) (void);
    vector<MHT_PTR> _threaded_methods;
    vector<string> _thread_labels;
    MHT_PTR _stop_f_pt;
    vector<T*> _elements;
    vector<T*> _performDelete;
    vector<CThreadInfo<T>*> _threadlds;
    vector<pthread_t*> _threads;
    int _totalRunningThreads;

    static void * gencker_(void * pArg)
    {
      CThreadInfo<T>* vArg = (CThreadInfo<T> *) pArg;
      vArg->_mutex.lock();
      int vIndex = vArg->_idx++;
      vArg->_mutex.unlock();

      vArg->_status_flags[vIndex]=true;

      MHT_PTR mhtCalledOne = vArg->_threaded_methods[vIndex];
      (vArg->_data->*mhtCalledOne)();
      vArg->_status_flags[vIndex]=false;
        return NULL;
    }

  public:
    CSThread ():_stop_f_pt(NULL), _totalRunningThreads(0)  {}
    ~CSThread()
    {
      for (int i=_threads.size() -1; i >= 0; --i)
          pthread_detach(*_threads[i]);

      for (int i=_threadlds.size() -1; i >= 0; --i)
        delete _threadlds[i];

      for (int i=_elements.size() -1; i >= 0; --i)
         if (find (_performDelete.begin(), _performDelete.end(), _elements[i]) != _performDelete.end())
              delete _elements[i];
    }
    int  runningThreadsCount(void) {return _totalRunningThreads;}
    int  elementsCount()        {return _elements.size();}
    void addThread (MHT_PTR p, string pLabel="") { _threaded_methods.push_back(p); _thread_labels.push_back(pLabel);}
    void clearThreadedMethods() { _threaded_methods.clear(); }
    void getThreadedMethodsCount() { return _threaded_methods.size(); }
    void addStopMethod(MHT_PTR p)  { _stop_f_pt  = p; }
    string getStatusStr(unsigned int _elementIndex, unsigned int pMethodIndex)
    {
      char ch[99];

      if (getStatus(_elementIndex, pMethodIndex) == true)
        sprintf (ch, "[%s] - TRUE\n", _thread_labels[pMethodIndex].c_str());
      else 
        sprintf (ch, "[%s] - FALSE\n", _thread_labels[pMethodIndex].c_str());

      return ch;
    }
    bool getStatus(unsigned int _elementIndex, unsigned int pMethodIndex)
    {
      if (_elementIndex > _elements.size()) return false;
      return _threadlds[_elementIndex]->_status_flags[pMethodIndex];
    }

    bool run(unsigned int pIdx) 
    {
      T * myElem = _elements[pIdx];
      _threadlds.push_back(new CThreadInfo<T>(myElem));
      _threadlds[_threadlds.size()-1]->setThreadedMethods(_threaded_methods);

      int vStart = _threads.size();
      for (int hhh=0; hhh<_threaded_methods.size(); ++hhh)
          _threads.push_back(new pthread_t);

      for (int currentCount =0; currentCount < _threaded_methods.size(); ++vStart, ++currentCount)
      {
                if (pthread_create(_threads[vStart], NULL, gencker_, (void*) _threadlds[_threadlds.size()-1]) != 0)
        {
                // cout <<"\t\tThread " << currentCount << " creation FAILED for element: " << pIdx << endl;
                    return false;
                }
        else
        {
            ++_totalRunningThreads;
             // cout <<"\t\tThread " << currentCount << " creation SUCCEDED for element: " << pIdx << endl;
                }
      }
      return true;
    }

    bool run() 
    {
            for (int vI = 0; vI < _elements.size(); ++vI) 
            if (run(vI) == false) return false;
          // cout <<"Number of currently running threads: " << _totalRunningThreads << endl;
        return true;
    }

    T * addElement(void)
    {
      int vId=-1;
      return addElement(vId);
    }

    T * addElement(int & pIdx)
    {
      T * myElem = new T();
      _elements.push_back(myElem);
      pIdx = _elements.size()-1;
      _performDelete.push_back(myElem);
      return _elements[pIdx];
    }

    T * addElement(T *pElem)
    {
      int vId=-1;
      return addElement(pElem, vId);
    }

    T * addElement(T *pElem, int & pIdx)
    {
      _elements.push_back(pElem);
      pIdx = _elements.size()-1;
      return pElem;
    }

    T * getElement(int pId) { return _elements[pId]; }

    void stopThread(int i)  
    {
      if (_stop_f_pt != NULL) 
      {
         ( _elements[i]->*_stop_f_pt)() ;
      }
      pthread_detach(*_threads[i]);
      --_totalRunningThreads;
    }

    void stopAll()  
    {
      if (_stop_f_pt != NULL) 
        for (int i=0; i<_elements.size(); ++i) 
        {
          ( _elements[i]->*_stop_f_pt)() ;
        }
      _totalRunningThreads=0;
    }
};
#endif
// end of test.h

// Un archivo de ejemplo de uso "test.cc" que en Linux he compilado con La clase que encapsula todo el trabajo para procesar un método: g ++ -o mytest.exe test.cc -I. -lpthread -lstdc ++

#include <test.h>
#include <vector>
#include <iostream>
#include <Mutex.h>

using namespace std;

// Just a class for which I need to "thread-ize" a some methods
// Given that with OOP the objecs include both "functions" (methods)
// and data (attributes), then there is no need to use function arguments,
// just a "void xxx (void)" method.
// 
class TPuck
{
  public:
   bool _go;
   TPuck(int pVal):_go(true)
   {
     Value = pVal;
   }
   TPuck():_go(true)
   {
   }
   int Value;
   int vc;

   void setValue(int p){Value = p; }

   void super()
   {
     while (_go)
     {
      cout <<"super " << vc << endl;
            sleep(2);
         }
      cout <<"end of super " << vc << endl;
   }

   void vusss()
   {
     while (_go)
     {
      cout <<"vusss " << vc << endl;
      sleep(2);
     }
      cout <<"end of vusss " << vc << endl;
   }

   void fazz()
   {
     static int vcount =0;
     vc = vcount++;
     cout <<"Puck create instance: " << vc << endl;
     while (_go)
     {
       cout <<"fazz " << vc << endl;
       sleep(2);
     }
     cout <<"Completed TPuck..fazz instance "<<  vc << endl;
   }

   void stop()
   {
      _go=false;
      cout << endl << "Stopping TPuck...." << vc << endl;
   }
};


int main(int argc, char* argv[])
{
  // just a number of instances of the class I need to make threads
  int vN = 3;

  // This object will be your threads maker.
  // Just declare an instance for each class
  // you need to create method threads
  //
  CSThread<TPuck> PuckThreadMaker;
  //
  // Hera I'm telling which methods should be threaded
  PuckThreadMaker.addThread(&TPuck::fazz, "fazz1");
  PuckThreadMaker.addThread(&TPuck::fazz, "fazz2");
  PuckThreadMaker.addThread(&TPuck::fazz, "fazz3");
  PuckThreadMaker.addThread(&TPuck::vusss, "vusss");
  PuckThreadMaker.addThread(&TPuck::super, "super");

  PuckThreadMaker.addStopMethod(&TPuck::stop);

  for (int ii=0; ii<vN; ++ii)
  {
    // Creating instances of the class that I need to run threads.
    // If you already have your instances, then just pass them as a
    // parameter such "mythreadmaker.addElement(&myinstance);"
    TPuck * vOne = PuckThreadMaker.addElement();
  }

  if (PuckThreadMaker.run() == true)
  {
    cout <<"All running!" << endl;
  }
  else
  {
    cout <<"Error: not all threads running!" << endl;
  }

  sleep(1);
  cout <<"Totale threads creati: " << PuckThreadMaker.runningThreadsCount()  << endl;
  for (unsigned int ii=0; ii<vN; ++ii)
  {
    unsigned int kk=0;
    cout <<"status for element " << ii << " is " << PuckThreadMaker.getStatusStr(ii, kk++) << endl; 
    cout <<"status for element " << ii << " is " << PuckThreadMaker.getStatusStr(ii, kk++) << endl; 
    cout <<"status for element " << ii << " is " << PuckThreadMaker.getStatusStr(ii, kk++) << endl; 
    cout <<"status for element " << ii << " is " << PuckThreadMaker.getStatusStr(ii, kk++) << endl; 
    cout <<"status for element " << ii << " is " << PuckThreadMaker.getStatusStr(ii, kk++) << endl; 
  }

  sleep(2);
  PuckThreadMaker.stopAll();
  cout <<"\n\nAfter the stop!!!!" << endl;
  sleep(2);

  for (int ii=0; ii<vN; ++ii)
  {
    int kk=0;
    cout <<"status for element " << ii << " is " << PuckThreadMaker.getStatusStr(ii, kk++) << endl; 
    cout <<"status for element " << ii << " is " << PuckThreadMaker.getStatusStr(ii, kk++) << endl; 
    cout <<"status for element " << ii << " is " << PuckThreadMaker.getStatusStr(ii, kk++) << endl; 
    cout <<"status for element " << ii << " is " << PuckThreadMaker.getStatusStr(ii, kk++) << endl; 
    cout <<"status for element " << ii << " is " << PuckThreadMaker.getStatusStr(ii, kk++) << endl; 
  }

  sleep(5);
  return 0;
}

// End of test.cc
Vejestorio
fuente
0

Esta es una pregunta un poco antigua, pero es un problema muy común al que se enfrentan muchos. A continuación se muestra una forma simple y elegante de manejar esto usando std :: thread

#include <iostream>
#include <utility>
#include <thread>
#include <chrono>

class foo
{
    public:
        void bar(int j)
        {
            n = j;
            for (int i = 0; i < 5; ++i) {
                std::cout << "Child thread executing\n";
                ++n;
                std::this_thread::sleep_for(std::chrono::milliseconds(10));
            }
        }
        int n = 0;
};

int main()
{
    int n = 5;
    foo f;
    std::thread class_thread(&foo::bar, &f, n); // t5 runs foo::bar() on object f
    std::this_thread::sleep_for(std::chrono::milliseconds(20));
    std::cout << "Main Thread running as usual";
    class_thread.join();
    std::cout << "Final value of foo::n is " << f.n << '\n';
}

El código anterior también se encarga de pasar argumentos a la función del hilo.

Consulte el documento std :: thread para obtener más detalles.

pankaj
fuente
-1

Supongo que esto es porque C ++ está destrozado un poco porque le envía un puntero C ++, no un puntero a función C. Aparentemente hay una diferencia . Intenta hacer un

(void)(*p)(void) = ((void) *(void)) &c[0].print; //(check my syntax on that cast)

y luego enviando p.

También hice lo que estás haciendo con una función miembro, pero lo hice en la clase que la estaba usando y con una función estática , que creo que marcó la diferencia.

EdH
fuente
Intenté lo anterior, pero me está dando errores de sintaxis ... Intenté cambiarlo también ... Si tuviera la amabilidad de demostrar que usar pthread_create (...) podría ser útil
Angel.King.47