bir sınıftan pthread işlevi


86

Diyelim ki gibi bir sınıfım var

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

Ve sonra bir c vektörüm var

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

Şimdi, üzerinde bir konu oluşturmak istiyorum c.print();

Ve şu bana aşağıdaki sorunu veriyor: pthread_create(&t1, NULL, &c[0].print, NULL);

Hata Çıkışı: '3' bağımsız değişkeni için 'void * (tree_item ::) (void )' 'void * ( ) (void )' ile 'int pthread_create (pthread_t *, const pthread_attr_t *, void * ( ) (void ), geçersiz *) '

Yanıtlar:


148

Bunu yazdığınız şekilde yapamazsınız çünkü C ++ sınıfı üye işlevlerinin gizli bir thisparametresi vardır. pthread_create()Hangi değerin thiskullanılacağı hakkında hiçbir fikri yoktur , bu nedenle yöntemi bir işleve çevirerek derleyicide dolaşmaya çalışırsanız uygun tipte bir gösterge, bir segmetnasyon hatası alırsınız. Sınıfı thisönyüklemek için statik bir sınıf yöntemi ( parametresi olmayan ) veya düz bir sıradan işlev kullanmanız gerekir:

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

yukarıdaki vektörlerle aşağıdaki şekilde çalışır mı: pthread_create (& t, NULL, & C :: hello_helper, & vector_c [0]); ?
Angel.King. 47

Yukarıdaki yorumların hepsi faydalıdır, bir sorunu çözmek için hepsinden bir kombinasyon kullandım .. Hala dozlama, yapmaya çalıştığım kadar basittir ... Ama ne yazık ki sadece birini doğru olarak işaretleyebilirim, Aksi takdirde herkes alır kredi..Teşekkürler
Angel.King.47

Bu yanıta olumlu oy vermek istedim, ancak aşırı önyargıyla sonlandırılması gereken C tarzı dökümler kullanıyor. Aksi takdirde bu cevap doğrudur.
Chris Jester-Young

@Chris: Oyunculuk stilleri hakkında kutsal bir savaşa girmek istemiyorum, ancak bu örnekte C tarzı bir kadro kullanmak anlamsal olarak tamamen doğru.
Adam Rosenfield

2
@AdamRosenfield, zarfları birbirine bağlamak anlamsal olarak da mükemmeldir, ancak bu onu iyi bir stil yapmaz! xD
ACK_stoverflow

82

Bir iş parçacığını işlemenin en sevdiğim yolu, onu bir C ++ nesnesi içinde kapsüllemektir. İşte bir örnek:

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

Bunu kullanmak için, iş parçacığınızın olay döngüsünü içerecek şekilde uygulanan InternalThreadEntry () yöntemiyle MyThreadClass'ın bir alt sınıfını oluşturmalısınız. Elbette, iş parçacığı nesnesini silmeden önce iş parçacığı nesnesinde WaitForInternalThreadToExit () 'i çağırmanız gerekir (ve iş parçacığının gerçekten çıktığından emin olmak için bir mekanizmaya sahip olmanız gerekir, aksi takdirde WaitForInternalThreadToExit () asla geri dönmez)


1
Bu, yukarıdaki Sanal Sınıfın kullanımını anlamam için harika bir yoldur, ancak daha fazla sorunum var ... Tümünün bir vektöre yerleştirilmesi gereken diğer iş parçacıklarından ortaya çıkan iş parçacıklarım var. Ve sonra gidip tüm konuları birleştirmek için özyinelemeli bir döngü. Eminim bunu yapmak için beklemeyi uygun yerde arayarak da uygulayabilirim, Ama nereye gittiğimi görmeye çalışacağım
Angel.King. 47

4
Bu çözüm çok şık. Bundan sonra kullanacağım. Teşekkürler Jeremy Friesner. +1
Armada

merhaba Jeremy Friesner, InternalThreadEntry (aclass_ref & refobj) referansı nasıl iletilir? ne tür değişiklikler yapmalıyım?
sree

@sree Başvuruyu (veya bir işaretçiyi) MyThreadClass'a üye değişkeni olarak ekleyin; daha sonra InternalThreadEntry (), (void *) argümanı aracılığıyla geçirme konusunda endişelenmenize gerek kalmadan ona doğrudan erişebilir.
Jeremy Friesner

10

pthread_createAradığı imzayla eşleşen bir işlev vermeniz gerekecek . Geçtiğin şey işe yaramayacak.

Bunu yapmaktan hoşlandığınız statik işlevi uygulayabilirsiniz cve iş parçacığında bir örneğine başvurabilir ve istediğiniz şeyi çalıştırabilir. pthread_createsadece bir işlev işaretçisi değil, aynı zamanda "bağlam" için bir işaretçi alacak şekilde tasarlanmıştır. Bu durumda, bir örneğine bir işaretçi iletirsiniz c.

Örneğin:

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
ooo ne demek istediğini anlıyorum .. c işaretçisini geç, gotcha .. uygulayacak ve deneyecek
Angel.King.47

2

Yukarıdaki cevaplar iyidir, ancak benim durumumda, fonksiyonu statik hale getiren 1. yaklaşım işe yaramadı. Çıkış kodunu iş parçacığı işlevine taşımak için dönüştürmeye çalışıyordum, ancak bu kodun zaten statik olmayan sınıf üyelerine yapılan başvurular için çok fazla olması vardı. C ++ nesnesine kapsüllemenin ikinci çözümü çalışır, ancak bir iş parçacığı çalıştırmak için 3 seviyeli sarmalayıcılara sahiptir.

Mevcut C ++ yapısını - 'arkadaş' işlevini kullanan alternatif bir çözümüm vardı ve bu benim durumum için mükemmel çalıştı. 'Arkadaş'ı nasıl kullandığımın bir örneği (yukarıdaki aynı örnek, arkadaş kullanılarak nasıl kompakt bir forma dönüştürülebileceğini gösteren adlar için kullanacaktır)

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

Tabii ki, boost :: thread kullanabilir ve tüm bunlardan kaçınabiliriz, ancak C ++ kodunu boost kullanmamak için değiştirmeye çalışıyordum (kod sadece bu amaç için boost'a karşı bağlantı oluşturuyordu)


1

Birisine yararlı olacağı ümidiyle ilk cevabım: Şimdi bu eski bir soru ama bir TcpServer sınıfı yazarken yukarıdaki soruyla tamamen aynı hatayla karşılaştım ve pthreads kullanmaya çalışıyordum. Bu soruyu buldum ve neden olduğunu şimdi anlıyorum. Bunu yaptım:

#include <thread>

ileti dizisi çalıştırma yöntemi -> void* TcpServer::sockethandler(void* lp) {/*code here*/}

ve ona lambda diyorum -> std::thread( [=] { sockethandler((void*)csock); } ).detach();

bu bana temiz bir yaklaşım gibi görünüyor.


0

Bunu istediğini çözmenin yollarını birçok kez buldum, bence çok karmaşık. Örneğin, yeni sınıf türleri, bağlantı kitaplığı vb. Tanımlamalısınız. Bu yüzden, son kullanıcının temelde "void :: method (void)" olan her ne sınıf. Elbette uyguladığım bu çözüm genişletilebilir, geliştirilebilir vb. Bu nedenle, daha spesifik yöntemlere veya özelliklere ihtiyacınız varsa, bunları ekleyin ve lütfen beni döngüde tutmak için çok nazik olun.

İşte ne yaptığımı gösteren 3 dosya.

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

// Bir yöntemi evrelendirmek için tüm çalışmayı kapsüle eden sınıf (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

// Linux üzerinde bir kullanım örneği dosyası "test.cc" ile derlediğim tüm işi bir yöntemi iş parçacığına dahil eden sınıf: 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

Bu biraz eski bir soru, ancak birçok kişinin karşılaştığı çok yaygın bir sorundur. Aşağıdaki, std :: thread kullanarak bunu halletmenin basit ve zarif bir yoludur.

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

Yukarıdaki kod aynı zamanda evre işlevine argüman iletmeyi de sağlar.

Daha fazla ayrıntı için std :: thread belgesine bakın.


-1

Tahminimce bu, C ++ b / c tarafından biraz karıştırılıyor, ona bir C ++ işaretçisi değil, bir C işlevi işaretçisi gönderiyorsunuz. Görünüşe göre bir fark var . Yapmayı dene

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

ve sonra p gönderiliyor.

Bir üye işleviyle de yaptığınız şeyi yaptım, ancak onu kullanan sınıfta ve statik bir işlevle yaptım - ki bence fark yarattı.


Yukarıdakileri denedim ama bana sözdizimi hataları veriyor .. Bunu da değiştirmeye çalıştım ... pthread_create (...) 'i kullanmanın yardımcı olabileceğini gösterecek kadar nazik
olursanız

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.