функция pthread из класса - PullRequest
73 голосов
/ 20 июля 2009

Допустим, у меня есть такой класс, как

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

И тогда у меня есть вектор с

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

Теперь я хочу создать тему на c.print();

И следующее дает мне проблему ниже: pthread_create(&t1, NULL, &c[0].print, NULL);

Ошибка Ouput: невозможно преобразовать «void * (tree_item :: ) (void )» в «void *» () (void ) ’для аргумента« 3 »для int pthread_create (pthread_t *, const pthread_attr_t *, void * () (void ), void *) ’

Ответы [ 8 ]

138 голосов
/ 20 июля 2009

Вы не можете сделать это так, как написали, потому что функции-члены класса C ++ имеют скрытый передаваемый параметр this. pthread_create() не знает, какое значение this использовать, поэтому, если вы попытаетесь чтобы обойти компилятор путем приведения метода к указателю на функцию соответствующего типа, вы получите ошибку сегментации. Вы должны использовать статический метод класса (у которого нет параметра this) или обычную обычную функцию для начальной загрузки класса:

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);
75 голосов
/ 20 июля 2009

Мой любимый способ обработки потока - заключать его в объект C ++. Вот пример:

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

Чтобы использовать его, вы просто должны создать подкласс MyThreadClass с методом InternalThreadEntry (), реализованным для хранения цикла событий вашего потока. Конечно, вам нужно вызвать WaitForInternalThreadToExit () для объекта потока перед удалением объекта потока (и иметь некоторый механизм, чтобы убедиться, что поток действительно завершился, иначе WaitForInternalThreadToExit () никогда не вернется)

8 голосов
/ 20 июля 2009

Вы должны будете дать pthread_create функцию, которая соответствует сигнатуре, которую он ищет. То, что вы передаете, не сработает.

Вы можете реализовать любую статическую функцию, которая вам нравится, и она может ссылаться на экземпляр c и выполнять то, что вы хотите в потоке. pthread_create предназначен для получения не только указателя функции, но и указателя на «контекст». В этом случае вы просто передаете ему указатель на экземпляр c.

Например:

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


void func() {

    ...

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

    ...
}
2 голосов
/ 17 июля 2015

Приведенные выше ответы хороши, но в моем случае первый подход, который преобразует функцию в статическую, не работает. Я пытался преобразовать существующий код, чтобы перейти в функцию потока, но в этом коде уже было много ссылок на нестатические члены класса. Второе решение инкапсуляции в объект C ++ работает, но имеет 3-х уровневые оболочки для запуска потока.

У меня было альтернативное решение, в котором используется существующая конструкция C ++ - функция «друг», и оно отлично подошло для моего случая. Пример того, как я использовал «друг» (будет использовать приведенный выше пример для имен, показывающих, как его можно преобразовать в компактную форму с помощью друга)

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

Конечно, мы можем использовать boost :: thread и избегать всего этого, но я пытался изменить код C ++, чтобы он не использовал boost (код был связан с boost только для этой цели)

1 голос
/ 30 августа 2016

Мой первый ответ в надежде, что он кому-нибудь пригодится: Теперь это старый вопрос, но я столкнулся с той же ошибкой, что и предыдущий вопрос, так как я пишу класс TcpServer и пытался использовать pthreads. Я нашел этот вопрос, и теперь я понимаю, почему это происходит. Я закончил тем, что сделал это:

#include <thread>

метод для работы с резьбой -> void* TcpServer::sockethandler(void* lp) {/*code here*/}

и я называю это лямбдой -> std::thread( [=] { sockethandler((void*)csock); } ).detach();

это кажется чистым подходом для меня.

0 голосов
/ 31 января 2018

Слишком много раз я находил способы решить то, о чем вы просите, на мой взгляд, слишком сложно. Например, вы должны определить новые типы классов, библиотеку ссылок и т. Д. Поэтому я решил написать несколько строк кода, которые позволят конечному пользователю в основном, чтобы иметь возможность "thread-ize" "void :: method (void)" любого класса. Конечно, это решение, которое я реализовал, может быть расширено, улучшено и т. Д., Так что если вам нужны более конкретные методы или функции, добавьте их и будьте любезны держать меня в курсе.

Вот 3 файла, которые показывают, что я сделал.

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

// Класс, который инкапсулирует всю работу для потокового метода (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

// Файл примера использования "test.cc", с которым я скомпилировал в linux Класс, который инкапсулирует всю работу для потокового метода: 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 голосов
/ 23 августа 2017

C ++: Как передать функцию-член класса в pthread_create ()?

http://thispointer.com/c-how-to-pass-class-member-function-to-pthread_create/

typedef void * (*THREADFUNCPTR)(void *);

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

pthread_create(&threadId, NULL, (THREADFUNCPTR) &C::print, NULL);
0 голосов
/ 20 июля 2009

Полагаю, это будет потому, что C / B немного искажается с помощью C + + B / C, когда вы посылаете ему указатель C ++, а не указатель на функцию C. По-видимому, разница . Попробуйте сделать

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

и затем отправка p.

Я сделал то же, что и вы, с помощью функции-члена, но я сделал это в классе, который его использовал, и с статической функцией - что, я думаю, имело значение.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...