функция pthread из класса

86

Скажем, у меня есть такой класс, как

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

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

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

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

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

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

Ангел.Кинг.47
источник

Ответы:

148

Вы не можете сделать это так, как вы написали, потому что функции-члены класса 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);
Адам Розенфилд
источник
будет ли вышеуказанное работать с векторами следующим образом: pthread_create (& t, NULL, & C :: hello_helper, & vector_c [0]); ?
Angel.King.47
Все приведенные выше комментарии полезны. Я использовал комбинацию из всех, чтобы решить проблему. Он по-прежнему dosent так же просто, как я пытался это сделать ... Но, к сожалению, я могу отметить только один как правильный, в противном случае каждый получит кредит
..
Я хотел проголосовать за этот ответ, но он использует приведение в стиле C, которое должно быть прекращено с крайними предубеждениями. В остальном этот ответ верен.
Крис Джестер-Янг
@Chris: Я не хочу ввязываться в священную войну по поводу стилей приведения, но в данном случае совершенно семантически правильно использовать приведение в стиле C.
Адам Розенфилд
2
@AdamRosenfield также совершенно семантически правильно связывать наречия вместе, но это не делает это хорошим стилем! xD
ACK_stoverflow
82

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

Джереми Фриснер
источник
1
Это отличный способ понять использование вышеупомянутого виртуального класса, но у меня гораздо более серьезные проблемы ... У меня есть потоки, которые порождают другие потоки, которые необходимо поместить в вектор. А затем рекурсивный цикл для присоединения всех потоков. Я уверен, что смогу реализовать вышеупомянутое, чтобы сделать то же самое, вызвав wait в нужном месте, но я попробую посмотреть, куда я доберусь
Angel.King.47
4
Это решение очень элегантно. Я буду им пользоваться. Спасибо, Джереми Фриснер. +1
Armada
привет Джереми Фриснер, как передать ссылку на InternalThreadEntry (aclass_ref & refobj)? какие изменения я должен внести?
sree
@sree Добавить ссылку (или указатель) на MyThreadClass в качестве переменной-члена; тогда InternalThreadEntry () может получить к нему доступ напрямую, не беспокоясь о передаче его через аргумент (void *).
Джереми Фриснер
10

Вам нужно будет указать 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]);

    ...
}
Джаред Оберхаус
источник
1
ооо, я понимаю, что вы имеете в виду ... передайте ему указатель c, gotcha ... реализует и
опробует
2

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

У меня было альтернативное решение, использующее существующую конструкцию 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

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

#include <thread>

метод для запуска с потоком -> void* TcpServer::sockethandler(void* lp) {/*code here*/}

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

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

ZoOl007
источник
0

Слишком много раз я находил способы решить то, о чем вы просите, на мой взгляд, это слишком сложно. Например, вам нужно определить новые типы классов, библиотеку ссылок и т. Д. Поэтому я решил написать несколько строк кода, которые позволяют конечному пользователю в основном иметь возможность «распределять потоки» для «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

Это немного старый вопрос, но очень распространенная проблема, с которой многие сталкиваются. Ниже приводится простой и элегантный способ справиться с этим с помощью 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';
}

Приведенный выше код также заботится о передаче аргумента функции потока.

Обратитесь std :: thread для более подробной информации.

панкадж
источник
-1

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

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

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

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

EdH
источник
Я пробовал описанное выше, но это дает мне синтаксические ошибки. Пытался исправить и это ... Если вы будете достаточно любезны, покажите, что с помощью pthread_create (...) это может быть полезно
Angel.King.47