pthread function from a class

前端 未结 9 1062
天命终不由人
天命终不由人 2020-11-22 01:00

Let\'s say I have a class such as

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

And then I have a vector of c

相关标签:
9条回答
  • 2020-11-22 01:26

    My guess would be this is b/c its getting mangled up a bit by C++ b/c your sending it a C++ pointer, not a C function pointer. There is a difference apparently. Try doing a

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

    and then sending p.

    I've done what your doing with a member function also, but i did it in the class that was using it, and with a static function - which i think made the difference.

    0 讨论(0)
  • 2020-11-22 01:27

    You can't do it the way you've written it because C++ class member functions have a hidden this parameter passed in. pthread_create() has no idea what value of this to use, so if you try to get around the compiler by casting the method to a function pointer of the appropriate type, you'll get a segmetnation fault. You have to use a static class method (which has no this parameter), or a plain ordinary function to bootstrap the class:

    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);
    
    0 讨论(0)
  • 2020-11-22 01:28

    Too many times I've found ways to solve what you are asking for that, in my opinion are too complicated. For instance you have to define new class types, link library etc. So I decided to write a few lines of code that allow the end user to basically be able to "thread-ize" a "void ::method(void)" of whatever class. For sure this solution I implemented can be extended, improved etc, so, if you need more specific methods or features, add them and please be so kind to keep me in the loop.

    Here are 3 files that show what I did.

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

    // The class that incapsulates all the work to thread-ize a method (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
    

    // A usage example file "test.cc" that on linux I've compiled with The class that incapsulates all the work to thread-ize a method: 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 讨论(0)
  • 2020-11-22 01:35

    My favorite way to handle a thread is to encapsulate it inside a C++ object. Here's an example:

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

    To use it, you would just create a subclass of MyThreadClass with the InternalThreadEntry() method implemented to contain your thread's event loop. You'd need to call WaitForInternalThreadToExit() on the thread object before deleting the thread object, of course (and have some mechanism to make sure the thread actually exits, otherwise WaitForInternalThreadToExit() would never return)

    0 讨论(0)
  • 2020-11-22 01:37

    You'll have to give pthread_create a function that matches the signature it's looking for. What you're passing won't work.

    You can implement whatever static function you like to do this, and it can reference an instance of c and execute what you want in the thread. pthread_create is designed to take not only a function pointer, but a pointer to "context". In this case you just pass it a pointer to an instance of c.

    For instance:

    static void* execute_print(void* ctx) {
        c* cptr = (c*)ctx;
        cptr->print();
        return NULL;
    }
    
    
    void func() {
    
        ...
    
        pthread_create(&t1, NULL, execute_print, &c[0]);
    
        ...
    }
    
    0 讨论(0)
  • 2020-11-22 01:41

    This is a bit old question but a very common issue which many face. Following is a simple and elegant way to handle this by using 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';
    }
    

    Above code also takes care of passing argument to the thread function.

    Refer std::thread document for more details.

    0 讨论(0)
提交回复
热议问题