funzione pthread da una classe


86

Diciamo che ho una classe come

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

E poi ho un vettore di c

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

Ora, voglio creare un thread su c.print();

E quanto segue mi dà il problema di seguito: pthread_create(&t1, NULL, &c[0].print, NULL);

Errore di uscita: impossibile convertire 'void * (tree_item ::) (void )' a 'void * ( ) (void )' per l'argomento '3' a 'int pthread_create (pthread_t *, const pthread_attr_t *, void * ( ) (void ), void *) "

Risposte:


148

Non puoi farlo nel modo in cui l'hai scritto perché le funzioni membro della classe C ++ hanno un thisparametro nascosto passato. pthread_create()Non ha idea di quale valore thisusare, quindi se provi a aggirare il compilatore eseguendo il cast del metodo su una funzione puntatore del tipo appropriato, otterrai un errore di segmentazione. Devi usare un metodo di classe statico (che non ha thisparametri) o una semplice funzione ordinaria per avviare la classe:

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);

funzionerebbe con i vettori nel modo seguente: pthread_create (& t, NULL, & C :: hello_helper, & vector_c [0]); ?
Angel.King.47

Tutti i commenti sopra sono utili, ho usato una combinazione di tutti per risolvere un problema .. È comunque semplice come stavo cercando di farlo ... Ma sfortunatamente posso contrassegnare solo uno come corretto, altrimenti ognuno ottiene il credito
..

Volevo dare un voto positivo a questa risposta, ma utilizza cast in stile C, che devono essere terminati con estremo pregiudizio. Questa risposta è altrimenti corretta.
Chris Jester-Young

@ Chris: Non voglio entrare in una guerra santa sugli stili di cast, ma è perfettamente semanticamente corretto usare un cast in stile C in questo caso.
Adam Rosenfield

2
@AdamRosenfield è anche perfettamente semanticamente corretto concatenare gli avverbi, ma questo non lo rende un buon stile! xD
ACK_stoverflow

82

Il mio modo preferito per gestire un thread è incapsularlo in un oggetto C ++. Ecco un esempio:

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;
};

Per usarlo, devi semplicemente creare una sottoclasse di MyThreadClass con il metodo InternalThreadEntry () implementato per contenere il ciclo di eventi del tuo thread. Dovresti chiamare WaitForInternalThreadToExit () sull'oggetto thread prima di eliminare l'oggetto thread, ovviamente (e avere un meccanismo per assicurarti che il thread esca effettivamente, altrimenti WaitForInternalThreadToExit () non tornerebbe mai)


1
Questo è un ottimo modo per capire l'uso della classe virtuale di cui sopra, ma ho molti problemi sordi. Ho thread che generano altri thread che devono essere tutti messi in un vettore. E poi un ciclo ricorsivo per andare a unire tutti i thread. Sono sicuro di poter implementare quanto sopra per farlo anche chiamando l'attesa nel posto giusto, ma lo provo per vedere dove arrivo
Angel.King.47

4
Questa soluzione è così molto elegante. Lo userò d'ora in poi. Grazie Jeremy Friesner. +1
Armada

ciao Jeremy Friesner, come passare un riferimento a InternalThreadEntry (aclass_ref & refobj)? quali modifiche dovrei apportare?
sree

@sree Aggiungere il riferimento (o un puntatore) a MyThreadClass come variabile membro; quindi InternalThreadEntry () può accedervi direttamente, senza doversi preoccupare di passarlo tramite l'argomento (void *).
Jeremy Friesner

10

Dovrai fornire pthread_createuna funzione che corrisponda alla firma che sta cercando. Quello che stai passando non funzionerà.

Puoi implementare qualsiasi funzione statica ti piaccia e può fare riferimento a un'istanza di ced eseguire ciò che desideri nel thread. pthread_createè progettato per accettare non solo un puntatore a funzione, ma un puntatore al "contesto". In questo caso gli basta passare un puntatore a un'istanza di c.

Per esempio:

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 capisco cosa intendi ..
passagli

2

Le risposte di cui sopra sono buone, ma nel mio caso, il primo approccio che converte la funzione in statica non ha funzionato. Stavo cercando di convertire il codice in uscita per passare alla funzione thread, ma quel codice aveva già molti riferimenti a membri della classe non statici. La seconda soluzione di incapsulamento in un oggetto C ++ funziona, ma ha wrapper a 3 livelli per eseguire un thread.

Avevo una soluzione alternativa che utilizza il costrutto C ++ esistente - la funzione "amico" e ha funzionato perfettamente per il mio caso. Un esempio di come ho usato 'amico' (userò lo stesso esempio sopra per i nomi che mostrano come può essere convertito in una forma compatta usando amico)

    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;
    }

Naturalmente, possiamo usare boost :: thread ed evitare tutto questo, ma stavo cercando di modificare il codice C ++ per non utilizzare boost (il codice si collegava a boost solo per questo scopo)


1

La mia prima risposta in assoluto nella speranza che possa essere utile a qualcuno: ora questa è una vecchia domanda ma ho riscontrato esattamente lo stesso errore della domanda precedente mentre scrivevo una classe TcpServer e stavo cercando di usare pthreads. Ho trovato questa domanda e ora capisco perché stava accadendo. Ho finito per fare questo:

#include <thread>

metodo per eseguire threaded -> void* TcpServer::sockethandler(void* lp) {/*code here*/}

e lo chiamo con un lambda -> std::thread( [=] { sockethandler((void*)csock); } ).detach();

questo mi sembra un approccio pulito.


0

Troppe volte ho trovato il modo per risolvere quello che mi chiedi, secondo me sono troppo complicati. Per esempio devi definire nuovi tipi di classi, librerie di link ecc. Quindi ho deciso di scrivere alcune righe di codice che permettano all'utente finale di essere fondamentalmente in grado di "thread-ize" un "void :: method (void)" qualunque classe. Sicuramente questa soluzione che ho implementato può essere estesa, migliorata ecc., Quindi, se hai bisogno di metodi o funzionalità più specifici, aggiungili e sii così gentile da tenermi aggiornato.

Ecco 3 file che mostrano cosa ho fatto.

    // 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 classe che incapsula tutto il lavoro per il thread-ize di un metodo (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 file di esempio di utilizzo "test.cc" che su linux ho compilato con La classe che incapsula tutto il lavoro per threadizzare un metodo: 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

0

Questa è una domanda un po 'vecchia ma un problema molto comune che molti devono affrontare. Di seguito è riportato un modo semplice ed elegante per gestirlo utilizzando 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';
}

Il codice sopra si occupa anche di passare argomenti alla funzione thread.

Fare riferimento al documento std :: thread per maggiori dettagli.


-1

La mia ipotesi sarebbe che questo sia b / c che viene alterato un po 'da C ++ b / c che gli mandi un puntatore C ++, non un puntatore a funzione C. Apparentemente c'è una differenza . Prova a fare un

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

e poi inviando p.

Ho fatto anche quello che fai tu con una funzione membro, ma l'ho fatto nella classe che la stava usando e con una funzione statica , che penso abbia fatto la differenza.


Ho provato quanto sopra ma mi dà errori di sintassi .. Ho provato a cambiarlo anche in giro ... Se fossi così gentile da mostrare che usando pthread_create (...) potrebbe essere utile
Angel.King.47

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.