fungsi pthread dari kelas

86

Katakanlah saya memiliki kelas seperti

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

Dan kemudian saya memiliki vektor c

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

Sekarang, saya ingin membuat utas c.print();

Dan berikut ini adalah masalah saya di bawah ini: pthread_create(&t1, NULL, &c[0].print, NULL);

Kesalahan Ouput: tidak dapat mengubah 'void * (tree_item ::) (void )' menjadi 'void * ( ) (void )' untuk argumen '3' menjadi 'int pthread_create (pthread_t *, const pthread_attr_t *, void * ( ) (void ), kosong*)'

Raja Malaikat. 47
sumber

Jawaban:

148

Anda tidak dapat melakukannya seperti yang Anda tulis karena fungsi anggota kelas C ++ memiliki thisparameter tersembunyi yang diteruskan. pthread_create()Tidak tahu nilai apa yang thisakan digunakan, jadi jika Anda mencoba menyiasati kompiler dengan mentransmisikan metode ke fungsi penunjuk jenis yang sesuai, Anda akan mendapatkan kesalahan segmetnation. Anda harus menggunakan metode kelas statis (yang tidak memiliki thisparameter), atau fungsi biasa biasa untuk mem-bootstrap kelas:

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
sumber
akan bekerja di atas vektor dengan cara berikut: pthread_create (& t, NULL, & C :: hello_helper, & vector_c [0]); ?
Angel.King. 47
Semua komentar di atas berguna jika saya menggunakan kombinasi dari semua untuk menyelesaikan masalah .. Itu masih tidak sesederhana yang saya coba lakukan ... Tapi sayangnya saya hanya dapat menandai satu sebagai yang benar, Jika tidak setiap orang mendapat kredit .. Terima kasih
Angel.King. 47
Saya ingin memberi suara positif pada jawaban ini, tetapi ini menggunakan gips bergaya C, yang harus diakhiri dengan prasangka ekstrem. Jawaban ini sebaliknya benar.
Chris Jester-Young
@ Chris: Saya tidak ingin terlibat dalam perang suci tentang gaya pemeran, tetapi secara semantik benar menggunakan pemeran gaya-C dalam hal ini.
Adam Rosenfield
2
@AdamRosenfield juga secara semantik benar untuk merangkai kata keterangan bersama, tetapi itu tidak menjadikannya gaya yang baik! xD
ACK_stoverflow
82

Cara favorit saya untuk menangani utas adalah merangkumnya di dalam objek C ++. Berikut contohnya:

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

Untuk menggunakannya, Anda cukup membuat subkelas MyThreadClass dengan metode InternalThreadEntry () yang diimplementasikan untuk memuat loop peristiwa thread Anda. Anda harus memanggil WaitForInternalThreadToExit () pada objek utas sebelum menghapus objek utas, tentu saja (dan memiliki beberapa mekanisme untuk memastikan utas benar-benar keluar, jika tidak WaitForInternalThreadToExit () tidak akan pernah kembali)

Jeremy Friesner
sumber
1
Itu adalah cara terbaik saya dapat memahami penggunaan Kelas Virtual di atas, Tetapi saya memiliki banyak masalah deaper .. Saya memiliki utas yang menelurkan utas lain yang perlu dimasukkan ke dalam vektor. Dan kemudian loop rekursif untuk pergi dan bergabung dengan semua utas. Saya yakin saya bisa menerapkan hal di atas untuk melakukannya juga dengan memanggil tunggu di tempat yang tepat, Tetapi saya akan mencobanya untuk melihat ke mana saya bisa
Angel.King. 47
4
Solusi ini sangat elegan. Saya akan menggunakannya mulai sekarang. Terima kasih Jeremy Friesner. +1
Armada
halo Jeremy Friesner, bagaimana cara menyampaikan referensi ke InternalThreadEntry (aclass_ref & refobj)? perubahan apa yang harus saya lakukan?
sree
@sree Tambahkan referensi (atau pointer) ke MyThreadClass sebagai variabel anggota; maka InternalThreadEntry () dapat mengaksesnya secara langsung, tanpa harus khawatir tentang meneruskannya melalui argumen (void *).
Jeremy Friesner
10

Anda harus memberikan pthread_createfungsi yang cocok dengan tanda tangan yang dicari. Apa yang Anda lewati tidak akan berhasil.

Anda dapat menerapkan fungsi statis apa pun yang Anda suka untuk melakukan ini, dan fungsi tersebut dapat merujuk ke instance cdan menjalankan apa yang Anda inginkan di utas. pthread_createdirancang untuk tidak hanya mengambil penunjuk fungsi, tetapi juga penunjuk ke "konteks". Dalam hal ini Anda hanya meneruskannya ke sebuah pointer ke sebuah instance c.

Misalnya:

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
sumber
1
ooo saya mengerti maksud Anda .. berikan pointer dari c, gotcha .. akan menerapkan dan mencobanya
Angel.King.47
2

Jawaban di atas bagus, tetapi dalam kasus saya, pendekatan pertama yang mengubah fungsi menjadi statis tidak berfungsi. Saya mencoba mengubah kode yang keluar untuk pindah ke fungsi utas tetapi kode itu sudah banyak referensi ke anggota kelas non-statis. Solusi kedua untuk mengenkapsulasi ke dalam objek C ++ berfungsi, tetapi memiliki pembungkus 3 tingkat untuk menjalankan utas.

Saya memiliki solusi alternatif yang menggunakan konstruksi C ++ yang ada - fungsi 'teman', dan itu berfungsi sempurna untuk kasus saya. Contoh bagaimana saya menggunakan 'teman' (akan menggunakan contoh yang sama di atas untuk nama yang menunjukkan bagaimana itu dapat diubah menjadi bentuk ringkas menggunakan teman)

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

Tentu saja, kita dapat menggunakan boost :: thread dan menghindari semua ini, tetapi saya mencoba mengubah kode C ++ agar tidak menggunakan boost (kode itu ditautkan ke boost hanya untuk tujuan ini)

sanmara
sumber
1

Jawaban pertama saya dengan harapan akan berguna bagi seseorang: Saya sekarang ini adalah pertanyaan lama tetapi saya menemukan kesalahan yang persis sama dengan pertanyaan di atas saat saya menulis kelas TcpServer dan saya mencoba menggunakan pthreads. Saya menemukan pertanyaan ini dan sekarang saya mengerti mengapa itu terjadi. Saya akhirnya melakukan ini:

#include <thread>

metode untuk menjalankan threaded -> void* TcpServer::sockethandler(void* lp) {/*code here*/}

dan saya menyebutnya dengan lambda -> std::thread( [=] { sockethandler((void*)csock); } ).detach();

itu tampaknya pendekatan yang bersih bagi saya.

ZoOl007
sumber
0

Terlalu sering saya menemukan cara untuk menyelesaikan apa yang Anda minta, menurut saya terlalu rumit. Misalnya Anda harus menentukan jenis kelas baru, pustaka tautan, dll. Jadi saya memutuskan untuk menulis beberapa baris kode yang memungkinkan pengguna akhir pada dasarnya dapat "thread-ize" a "void :: method (void)" dari kelas apapun. Yang pasti solusi yang saya terapkan ini dapat diperpanjang, ditingkatkan dll, jadi, jika Anda memerlukan metode atau fitur yang lebih spesifik, tambahkan mereka dan mohon berbaik hati agar saya tetap terhubung.

Berikut adalah 3 file yang menunjukkan apa yang saya lakukan.

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

// Kelas yang memasukkan semua pekerjaan ke thread-ize sebuah metode (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

// File contoh penggunaan "test.cc" yang di linux telah saya kompilasi dengan Kelas yang menyertakan semua pekerjaan untuk membuat utas metode: 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
sumber
0

Ini adalah pertanyaan yang agak lama tetapi masalah yang sangat umum yang dihadapi banyak orang. Berikut ini adalah cara sederhana dan elegan untuk menangani ini dengan menggunakan 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';
}

Kode di atas juga menangani pengiriman argumen ke fungsi utas.

Lihat dokumen std :: thread untuk lebih jelasnya.

pankaj
sumber
-1

Dugaan saya akan ini adalah b / c yang semakin rusak sedikit oleh C ++ b / c Anda mengirimkannya pointer C ++, bukan pointer fungsi C. Ternyata ada perbedaan . Coba lakukan

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

dan kemudian mengirim p.

Saya telah melakukan apa yang Anda lakukan dengan fungsi anggota juga, tetapi saya melakukannya di kelas yang menggunakannya, dan dengan fungsi statis - yang menurut saya membuat perbedaan.

EdH
sumber
Saya mencoba cara di atas tetapi ini memberi saya kesalahan sintaks .. Mencoba mengubahnya juga ... Jika Anda berbaik hati menunjukkan bahwa menggunakan pthread_create (...) mungkin membantu
Angel.King.47