pthread-Funktion aus einer Klasse

82

Nehmen wir an, ich habe eine Klasse wie

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

Und dann habe ich einen Vektor von c

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

Jetzt möchte ich einen Thread erstellen c.print();

Und das Folgende gibt mir das folgende Problem: pthread_create(&t1, NULL, &c[0].print, NULL);

Fehlerausgabe: 'void * (tree_item ::) (void )' kann nicht in 'void * ( ) (void )' für das Argument '3' in 'int pthread_create (pthread_t *, const pthread_attr_t *, void * ( ) (void) konvertiert werden ), nichtig *) '

Angel.King.47
quelle

Antworten:

146

Sie können es nicht so machen, wie Sie es geschrieben haben, da in C ++ - Klassenmitgliedsfunktionen ein versteckter thisParameter übergeben wurde. Sie wissen pthread_create()nicht, welchen Wert thissie verwenden sollen. Wenn Sie also versuchen, den Compiler zu umgehen, indem Sie die Methode in eine Funktion umwandeln Zeiger des entsprechenden Typs, erhalten Sie einen Segmetnationsfehler. Sie müssen eine statische Klassenmethode (die keinen thisParameter hat) oder eine normale Funktion verwenden, um die Klasse zu booten:

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
quelle
würde das obige mit Vektoren auf folgende Weise funktionieren: pthread_create (& t, NULL, & C :: hello_helper, & vector_c [0]); ?
Angel.King.47
Alle obigen Kommentare sind nützlich, ich habe eine Kombination von allen verwendet, um ein Problem zu lösen. Es ist immer noch so einfach, wie ich es versucht habe ... Aber leider kann ich nur einen als den richtigen markieren, sonst bekommt jeder der Kredit
..
Ich wollte diese Antwort positiv bewerten, aber sie verwendet Casts im C-Stil, die mit extremen Vorurteilen beendet werden müssen. Diese Antwort ist ansonsten richtig.
Chris Jester-Young
@Chris: Ich möchte nicht in einen heiligen Krieg um Besetzungsstile geraten, aber es ist absolut semantisch korrekt, in diesem Fall eine Besetzung im C-Stil zu verwenden.
Adam Rosenfield
2
@AdamRosenfield Es ist auch absolut semantisch korrekt, Adverbien miteinander zu verketten, aber das macht es nicht zu einem guten Stil! xD
ACK_stoverflow
81

Meine bevorzugte Art, mit einem Thread umzugehen, besteht darin, ihn in ein C ++ - Objekt zu kapseln. Hier ist ein Beispiel:

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

Um es zu verwenden, erstellen Sie einfach eine Unterklasse von MyThreadClass mit der InternalThreadEntry () -Methode, die so implementiert ist, dass sie die Ereignisschleife Ihres Threads enthält. Sie müssen natürlich WaitForInternalThreadToExit () für das Thread-Objekt aufrufen, bevor Sie das Thread-Objekt löschen (und über einen Mechanismus verfügen, um sicherzustellen, dass der Thread tatsächlich beendet wird, da WaitForInternalThreadToExit () sonst niemals zurückkehren würde).

Jeremy Friesner
quelle
1
Das ist eine großartige Möglichkeit, die Verwendung der oben genannten virtuellen Klasse zu verstehen, aber ich habe viel taubere Probleme. Ich habe Threads, die aus anderen Threads hervorgehen, die alle in einen Vektor eingefügt werden müssen. Und dann eine rekursive Schleife, um alle Threads zu verbinden. Ich bin mir sicher, dass ich das oben
Genannte
4
Diese Lösung ist so sehr elegant. Ich werde es von nun an verwenden. Vielen Dank, Jeremy Friesner. +1
Armada
Hallo Jeremy Friesner, wie übergebe ich einen Verweis auf InternalThreadEntry (aclass_ref & refobj)? Welche Änderungen sollte ich vornehmen?
Sree
@sree Fügen Sie der MyThreadClass die Referenz (oder einen Zeiger) als Mitgliedsvariable hinzu. dann kann InternalThreadEntry () direkt darauf zugreifen, ohne sich Gedanken über die Übergabe über das Argument (void *) machen zu müssen.
Jeremy Friesner
9

Sie müssen pthread_createeine Funktion angeben, die der gesuchten Signatur entspricht. Was Sie passieren, wird nicht funktionieren.

Sie können jede beliebige statische Funktion implementieren, die Sie dazu causführen möchten , und sie kann auf eine Instanz des Threads verweisen und diese ausführen. pthread_createwurde entwickelt, um nicht nur einen Funktionszeiger, sondern auch einen Zeiger auf "Kontext" aufzunehmen. In diesem Fall übergeben Sie ihm einfach einen Zeiger auf eine Instanz von c.

Zum Beispiel:

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
quelle
1
ooo ich verstehe was du meinst ..
gib
2

Die obigen Antworten sind gut, aber in meinem Fall hat der erste Ansatz, der die Funktion in eine statische konvertiert, nicht funktioniert. Ich habe versucht, bestehenden Code zu konvertieren, um ihn in die Thread-Funktion zu verschieben, aber dieser Code enthielt bereits viele Verweise auf nicht statische Klassenmitglieder. Die zweite Lösung zum Einkapseln in ein C ++ - Objekt funktioniert, verfügt jedoch über 3-Ebenen-Wrapper zum Ausführen eines Threads.

Ich hatte eine alternative Lösung, die das vorhandene C ++ - Konstrukt verwendet - die Funktion 'friend' - und sie funktionierte perfekt für meinen Fall. Ein Beispiel, wie ich 'Freund' verwendet habe (verwendet das obige Beispiel für Namen, die zeigen, wie es mit Freund in eine kompakte Form umgewandelt werden kann)

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

Natürlich können wir boost :: thread verwenden und all dies vermeiden, aber ich habe versucht, den C ++ - Code so zu ändern, dass kein Boost verwendet wird (der Code wurde nur zu diesem Zweck gegen Boost verlinkt).

Sanmara
quelle
1

Meine erste Antwort in der Hoffnung, dass sie für jemanden nützlich sein wird: Ich bin jetzt eine alte Frage, aber ich habe genau den gleichen Fehler wie die obige Frage festgestellt, als ich eine TcpServer-Klasse schrieb und versuchte, pthreads zu verwenden. Ich habe diese Frage gefunden und verstehe jetzt, warum es passiert ist. Am Ende habe ich das gemacht:

#include <thread>

Methode zum Ausführen von Threaded -> void* TcpServer::sockethandler(void* lp) {/*code here*/}

und ich nenne es mit einem Lambda -> std::thread( [=] { sockethandler((void*)csock); } ).detach();

das scheint mir ein sauberer Ansatz zu sein.

ZoOl007
quelle
0

Zu oft habe ich Wege gefunden, um das zu lösen, wonach Sie fragen. Meiner Meinung nach sind sie zu kompliziert. Zum Beispiel müssen Sie neue Klassentypen, Linkbibliotheken usw. definieren. Deshalb habe ich beschlossen, ein paar Codezeilen zu schreiben, die es dem Endbenutzer ermöglichen, im Grunde eine "void :: method (void)" von "thread-ize" zu können welche Klasse auch immer. Natürlich kann diese von mir implementierte Lösung erweitert, verbessert usw. werden. Wenn Sie also spezifischere Methoden oder Funktionen benötigen, fügen Sie diese hinzu und halten Sie mich auf dem Laufenden.

Hier sind 3 Dateien, die zeigen, was ich getan habe.

    // 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

// Die Klasse, die die gesamte Arbeit zum Thread-Ize einer Methode (test.h) einschließt:

#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

// Eine Verwendungsbeispieldatei "test.cc", die ich unter Linux kompiliert habe. Die Klasse, die die gesamte Arbeit zum Thread-ize einer Methode zusammenfasst: 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
Dodo
quelle
0

Dies ist eine etwas alte Frage, aber ein sehr häufiges Problem, mit dem viele konfrontiert sind. Das Folgende ist eine einfache und elegante Möglichkeit, dies mithilfe von std :: thread zu handhaben

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

Der obige Code sorgt auch dafür, dass Argumente an die Thread-Funktion übergeben werden.

Siehe std :: thread Dokument für weitere Details.

pankaj
quelle
-1

Meine Vermutung wäre, dass dies b / c ist, das durch C ++ b / c ein wenig verstümmelt wird, wenn Sie ihm einen C ++ - Zeiger senden, keinen C-Funktionszeiger. Es gibt anscheinend einen Unterschied . Versuchen Sie es mit einem

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

und dann senden p.

Ich habe getan, was Sie mit einer Mitgliedsfunktion tun, aber ich habe es in der Klasse getan, die es verwendet hat, und mit einer statischen Funktion - was meiner Meinung nach den Unterschied ausmachte.

EdH
quelle
Ich habe das oben Genannte versucht, aber es gibt mir Syntaxfehler. Ich habe versucht, es auch zu ändern ... Wenn Sie so freundlich wären, zu zeigen, dass die Verwendung von pthread_create (...) hilfreich sein könnte
Angel.King.47