2009-07-20 16 views
67

Digamos que tengo una clase comofunción pthread de una clase

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 el siguiente me da el problema a continuación: pthread_create(&t1, NULL, &c[0].print, NULL);

salida de la señal de error: 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 *, * vacío () (void), void *)'

Respuesta

125

No puede hágalo de la forma en que lo ha escrito porque las funciones miembro de la clase C++ tienen un parámetro this oculto pasado. pthread_create() no tiene idea qué valor de this usar, por lo que si intenta esquivar el compilador al fundir el método en un puntero de función del tipo apropiado, obtendrá una falla de segmentación.Usted tiene que utilizar un método estático de clase (que no tiene parámetros this), o una función ordinaria normal 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); 
+0

lo anterior funcionaría con vectores de la siguiente manera: pthread_create (& t, NULL, & C :: hello_helper, & vector_c [0]); ? –

+0

Todos los comentarios anteriores son útiles. Usé una combinación de todos para resolver un problema. Todavía dosent hace tan simple como yo estaba tratando de hacerlo ... Pero desafortunadamente solo puedo marcar uno como el correcto, de lo contrario cada uno obtiene el crédito .. Gracias –

+0

Quise votar esta respuesta, pero usa moldes de estilo C, que deben terminarse con extremo prejuicio. Esta respuesta es correcta. –

5

Usted tendrá que dar pthread_create una función que coincide 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 de c y ejecutar lo que desee en la secuencia. pthread_create está diseñado para tomar no solo un puntero de función, sino un puntero a "contexto". En este caso, solo 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]); 

    ... 
} 
+1

ooo veo lo que quieres decir ... pásalo por el puntero de c, gotcha ..lo implementaré y lo probaré –

-1

Mi conjetura sería esto es b/c su conseguir destrozado un poco de C++ B C++ su puntero, no un puntero de función C/enviándole un C. Hay un difference aparentemente. Intente hacer un

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

y luego enviar p.

He hecho lo que hace con una función miembro también, pero lo hice en la clase que lo estaba usando, y con una función estática - que creo que marcó la diferencia.

+0

Intenté lo anterior pero me está dando errores de sintaxis .. Intenté cambiarlo también ... Si tuviera la amabilidad de mostrar que usar pthread_create (...) podría ser útil –

64

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, sólo crearía una subclase de MiClaseThread con el método InternalThreadEntry() implementado para contener bucle de eventos de su hilo. Necesitaría llamar a WaitForInternalThreadToExit() en el objeto thread antes de eliminar el objeto thread, por supuesto (y tener algún mecanismo para asegurarse de que el hilo realmente sale, de lo contrario WaitForInternalThreadToExit() nunca volvería)

+2

sí. Hago esta misma técnica exacta. excelente manera de hacerlo ... – EdH

+1

Esa es una excelente manera de entender el uso de la clase virtual anterior, pero tengo muchos problemas de desactivación ... Tengo hilos que generan otros hilos que deben ponerse 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 dónde llego al –

+2

Diría que si es posible, use 'boost :: thread'. – GManNickG

1

Las respuestas anteriores son buenos, pero en mi caso, primero enfoque que convierte la función ser un estático no funcionó. Estaba tratando de convertir el código de salida 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 objetos de C++ funciona, pero tiene envoltorios de 3 niveles para ejecutar un hilo.

Tenía una solución alternativa que usa la construcción existente de C++ - función 'amigo', y funcionó perfecto para mi caso. Un ejemplo de cómo solía 'amigo' (usará el mismo ejemplo anterior para los nombres que muestra la forma en que 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; 
    } 

Ofcourse, podemos utilizar impulso :: hilo y evitar todo esto, pero yo estaba tratando de modificar el código C++ para no usar el impulso (el código estaba vinculando contra impulso solo para este propósito)

1

Mi primera respuesta siempre con la esperanza de que sea útil para alguien: I ahora esta es una vieja pregunta pero encontré exactamente el mismo error que la pregunta anterior ya que estoy escribiendo 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();

que parece un enfoque limpio para mí.

+0

Pidió hilos POSIX, no std :: hilos – gj13

0

Demasiadas veces me he encontrado maneras de resolver lo que está pidiendo que, en mi opinión, son demasiado complicados. Por ejemplo, tiene que definir nuevos tipos de clase, biblioteca de enlaces, etc. Así que decidí escribir unas líneas de código que permiten al usuario final básicamente ser capaz de "enhebrar" un método "vacío" (vacío) "de cualquier clase". Estoy seguro de que esta solución que implementé puede ampliarse, mejorarse, etc., así que, si necesita métodos o funciones más específicas, agréguelas y sea tan amable de mantenerme al tanto.

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 incapsulates todo el trabajo para enhebrar-ize 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 ejemplo de uso de archivos "test.cc" que en Linux I 've compilado con La clase que encapsula todo el trabajo para enhebrar 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