função pthread de uma classe

86

Digamos que eu tenha uma aula como

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

E então eu tenho um vetor de c

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

Agora, quero criar um tópico sobre c.print();

E o seguinte está me dando o problema abaixo: pthread_create(&t1, NULL, &c[0].print, NULL);

Erro Ouput: não é possível converter 'void * (tree_item ::) (void )' em 'void * ( ) (void )' para o argumento '3' em 'int pthread_create (pthread_t *, const pthread_attr_t *, void * ( ) (void ), vazio *) '

Angel.King.47
fonte

Respostas:

147

Você não pode fazer da maneira que escreveu porque as funções-membro da classe C ++ têm um thisparâmetro oculto transmitido. Não pthread_create()tem ideia de qual valor thisusar, então se você tentar contornar o compilador convertendo o método para uma função ponteiro do tipo apropriado, você obterá uma falha de segmentação. Você deve usar um método de classe estático (que não tem thisparâmetro) ou uma função comum simples para inicializar a 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);
Adam Rosenfield
fonte
o acima funcionaria com vetores da seguinte maneira: pthread_create (& t, NULL, & C :: hello_helper, & vector_c [0]); ?
Angel.King.47
Todos os comentários acima são úteis. Usei uma combinação de todos para resolver um problema. Ainda assim, é tão simples quanto eu estava tentando fazer ... Mas, infelizmente, só posso marcar um como o correto, caso contrário, todos entendem o crédito
..
Eu queria votar a favor dessa resposta, mas ele usa elencos no estilo C, que deve ser encerrado com extremo preconceito. Esta resposta está correta.
Chris Jester-Young
@Chris: Não quero entrar em uma guerra santa sobre estilos de elenco, mas é perfeitamente correto semanticamente usar um elenco de estilo C neste caso.
Adam Rosenfield
2
@AdamRosenfield também é semanticamente correto encadear advérbios, mas isso não o torna um bom estilo! xD
ACK_stoverflow
82

Minha maneira favorita de lidar com um thread é encapsulá-lo dentro de um objeto C ++. Aqui está um exemplo:

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 usá-lo, você apenas criaria uma subclasse de MyThreadClass com o método InternalThreadEntry () implementado para conter o loop de evento do thread. Você precisaria chamar WaitForInternalThreadToExit () no objeto thread antes de excluí-lo, é claro (e ter algum mecanismo para garantir que o thread realmente saia, caso contrário WaitForInternalThreadToExit () nunca retornaria)

Jeremy Friesner
fonte
1
Essa é uma ótima maneira de entender o uso da classe virtual acima, mas tenho problemas muito mais complexos ... Tenho threads que geram outros threads que precisam ser colocados em um vetor. E então um loop recursivo para ir e juntar todos os threads. Tenho certeza de que poderia implementar o acima para fazer isso também chamando a espera no lugar adequado, mas vou tentar para ver onde chego
Angel.King.47
4
Esta solução é muito elegante. Vou usar de agora em diante. Obrigado Jeremy Friesner. +1
Armada,
Olá Jeremy Friesner, como passar uma referência para InternalThreadEntry (aclass_ref & refobj)? que mudanças devo fazer?
sree
@sree Adiciona a referência (ou um ponteiro) a MyThreadClass como uma variável de membro; então InternalThreadEntry () pode acessá-lo diretamente, sem ter que se preocupar em passá-lo por meio do argumento (void *).
Jeremy Friesner
10

Você terá que fornecer pthread_createuma função que corresponda à assinatura que está procurando. O que você está passando não vai funcionar.

Você pode implementar qualquer função estática que desejar para fazer isso, e pode fazer referência a uma instância ce executar o que você quiser no thread. pthread_createfoi projetado para receber não apenas um ponteiro de função, mas um ponteiro para o "contexto". Nesse caso, você apenas passa um ponteiro para uma instância de c.

Por exemplo:

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
fonte
1
ooo, entendo o que você quer dizer ... passe o ponteiro de c, entendi ... implementarei e
testarei
2

As respostas acima são boas, mas no meu caso, a primeira abordagem que converte a função em estática não funcionou. Eu estava tentando converter o código de saída para mover para a função de thread, mas esse código já tinha muitas referências a membros de classe não estáticos. A segunda solução de encapsular em objetos C ++ funciona, mas tem wrappers de 3 níveis para executar um thread.

Eu tinha uma solução alternativa que usa a construção C ++ existente - função 'amigo', e funcionou perfeitamente para o meu caso. Um exemplo de como usei 'amigo' (usarei o mesmo exemplo acima para nomes que mostram como ele pode ser convertido em uma 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;
    }

Claro, podemos usar boost :: thread e evitar tudo isso, mas eu estava tentando modificar o código C ++ para não usar boost (o código estava vinculando a boost apenas para essa finalidade)

Sanmara
fonte
1

Minha primeira resposta na esperança de que seja útil para alguém: eu agora esta é uma pergunta antiga, mas encontrei exatamente o mesmo erro da pergunta acima, pois estou escrevendo uma classe TcpServer e estava tentando usar pthreads. Eu encontrei essa pergunta e agora entendo porque isso estava acontecendo. Acabei fazendo isso:

#include <thread>

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

e eu chamo de lambda -> std::thread( [=] { sockethandler((void*)csock); } ).detach();

isso parece uma abordagem limpa para mim.

ZoOl007
fonte
0

Muitas vezes encontrei maneiras de resolver o que você está pedindo que, na minha opinião, são muito complicadas. Por exemplo, você tem que definir novos tipos de classes, biblioteca de links etc. Então, decidi escrever algumas linhas de código que permitem ao usuário final basicamente ser capaz de "encadear" um "void :: method (void)" qualquer classe. Com certeza esta solução que implementei pode ser estendida, melhorada etc., então, se você precisar de métodos ou recursos mais específicos, adicione-os e por favor, tenha a gentileza de me manter informado.

Aqui estão 3 arquivos que mostram o que eu fiz.

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

// A classe que engloba todo o trabalho para encadear um 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

// Um ​​arquivo de exemplo de uso "test.cc" que compilei no linux com a classe que incapsula todo o trabalho para encadear um 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
Dodo
fonte
0

Esta é uma questão um pouco antiga, mas um problema muito comum que muitos enfrentam. A seguir está uma maneira simples e elegante de lidar com isso 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';
}

O código acima também se encarrega de passar o argumento para a função thread.

Consulte o documento std :: thread para mais detalhes.

pankaj
fonte
-1

Meu palpite seria que isso é b / c está ficando um pouco confuso por C ++ b / c você está enviando um ponteiro C ++, não um ponteiro de função C. Aparentemente, há uma diferença . Tente fazer um

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

e, em seguida, enviar p.

Também fiz o que você está fazendo com uma função de membro, mas fiz na classe que a estava usando e com uma função estática - que acho que fez a diferença.

EdH
fonte
Eu tentei o acima, mas está me dando erros de sintaxe .. Tentei alterá-lo também ... Se você fizer a gentileza de mostrar que usar o pthread_create (...) pode ser útil
Angel.King.47