функція 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 ), порожнеча *) '

Відповіді:


147

Ви не можете зробити це так, як написали, оскільки функції-члени класу 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

Всі вищезазначені коментарі корисні, я використовував комбінацію з усіх для вирішення проблеми .. Це все ще не так просто, як я намагався це зробити ... Але, на жаль, я можу позначити лише один як правильний, інакше кожен отримує кредит..Дякую
Angel.King.47

Я хотів підтримати цю відповідь, але в ній використовуються склади у стилі С, які повинні бути припинені з крайнім упередженням. Ця відповідь є правильною.
Chris Jester-Young

@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
Це чудовий спосіб зрозуміти використання вищезазначеного віртуального класу, але у мене набагато глибші проблеми .. У мене є потоки, які породжують інші потоки, які потрібно все розмістити у векторі. А потім рекурсивний цикл для об’єднання всіх потоків. Я впевнений, що міг би реалізувати вищезазначене, щоб також зробити це, зателефонувавши чекати у належному місці, але я спробую це побачити, куди я
діходжусь

4
Це рішення настільки елегантне. Я буду користуватися ним відтепер. Дякую Джеремі Фріснер. +1
Армада

привіт Джеремі Фрізнер, як передати посилання на 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
ооо, я розумію, що ти маєш на увазі ..
передай

2

Наведені вище відповіді хороші, але в моєму випадку перший підхід, який перетворює функцію на статичну, не спрацював. Я намагався перетворити вихідний код, щоб перейти у функцію потоку, але в цьому коді вже було багато посилань на нестатичні члени класу. Друге рішення інкапсуляції в об’єкт С ++ працює, але має 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

Моя перша відповідь коли-небудь в надії, що це буде корисно для когось: Зараз це старе запитання, але я зіткнувся з точно такою ж помилкою, як і вище, коли я писав клас TcpServer, і я намагався використовувати pthreads. Я знайшов це запитання і тепер розумію, чому це відбувалося. У підсумку я зробив це:

#include <thread>

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

і я називаю це лямбда -> std::thread( [=] { sockethandler((void*)csock); } ).detach();

це здається мені чистим підходом.


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

Я здогадуюсь, це б / с, яке його трохи зіпсує C ++ b / c, коли ви надсилаєте йому вказівник C ++, а не вказівник на функцію C. Очевидно, є різниця . Спробуйте зробити a

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

а потім надіслати с.

Я зробив те, що ви робите з функцією-членом, але я зробив це в класі, який її використовував, і зі статичною функцією - що, на мою думку, мало значення.


Я спробував вищезазначене, але це дало мені синтаксичні помилки .. Спробував це також змінити ... Якщо ви виявите люб'язність, щоб показати, що використання pthread_create (...) це може бути корисним
Angel.King.47

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.