클래스의 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);

오류 출력 : 인수 '3'에 대해 'void * (tree_item ::) (void )'를 'void * ( ) (void )' 로 변환 할 수 없습니다. 'int pthread_create (pthread_t *, const pthread_attr_t *, void * ( ) (void ), 무효 *) '

답변:


148

C ++ 클래스 멤버 함수에 숨겨진 this매개 변수가 전달 되었기 때문에 작성한 방식대로 수행 할 수 없습니다 . 사용할 pthread_create()값을 알지 못 this하므로 메소드를 함수로 캐스팅하여 컴파일러를 우회하려고합니다. 적절한 유형의 포인터를 사용하면 segmetnation 오류가 발생합니다. 정적 클래스 메서드 ( 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 2007

위의 모든 설명은 유용합니다. 문제를 해결하기 위해 모두의 조합을 사용했습니다 .. 그래도 시도했던 것만 큼 간단하지 않습니다.하지만 불행하게도 하나만 올바른 것으로 표시 할 수 있습니다. credit..Thanks
Angel.King.47

이 답변을 찬성하고 싶었지만 C 스타일의 캐스트를 사용하므로 극도의 편견으로 종료되어야합니다. 이 대답은 그렇지 않으면 정확합니다.
크리스 광대 - 젊은

@Chris : 저는 캐스트 스타일에 대한 거룩한 전쟁에 참여하고 싶지 않지만,이 경우 C 스타일 캐스트를 사용하는 것은 의미 상 완벽하게 옳습니다.
Adam Rosenfield 2011 년

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

이를 사용하려면 스레드의 이벤트 루프를 포함하도록 구현 된 InternalThreadEntry () 메서드를 사용하여 MyThreadClass의 하위 클래스를 만듭니다. 물론 스레드 개체를 삭제하기 전에 스레드 개체에서 WaitForInternalThreadToExit ()를 호출해야합니다 (그리고 스레드가 실제로 종료되는지 확인하는 메커니즘이 있습니다. 그렇지 않으면 WaitForInternalThreadToExit ()가 반환되지 않음)


1
그것은 위의 가상 클래스의 사용을 이해할 수있는 좋은 방법입니다. 그러나 저는 매우 심각한 문제가 있습니다. 벡터에 모두 넣어야하는 다른 스레드에서 생성되는 스레드가 있습니다. 그리고 모든 스레드를 연결하는 재귀 루프. 나는 적절한 장소에서 기다림을 호출하여 위의 방법을 구현할 수 있다고 확신합니다. 그러나 내가 어디로 가는지보기 위해 시도해보십시오
Angel.King.47

4
이 솔루션은 매우 우아합니다. 이제부터 사용하겠습니다. Jeremy Friesner 감사합니다. +1
Armada 2013 년

안녕하세요 Jeremy Friesner, InternalThreadEntry (aclass_ref & refobj)에 대한 참조를 전달하는 방법은 무엇입니까? 어떤 변경을해야합니까?
sree

@sree MyThreadClass에 대한 참조 (또는 포인터)를 멤버 변수로 추가합니다. 그러면 InternalThreadEntry ()가 (void *) 인수를 통해 전달하는 것에 대해 걱정할 필요없이 직접 액세스 할 수 있습니다.
Jeremy Friesner

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
ooo 무슨 뜻인지 알 겠어요 .. c의 포인터를 전달하면 .. 구현하고 시도해 볼 것입니다
Angel.King.47

2

위의 답변은 좋지만 제 경우에는 함수를 정적으로 변환하는 첫 번째 접근 방식이 작동하지 않았습니다. 스레드 함수로 이동하기 위해 기존 코드를 변환하려고 시도했지만 해당 코드에는 이미 비 정적 클래스 멤버에 대한 참조가 많이 있습니다. C ++ 객체로 캡슐화하는 두 번째 솔루션은 작동하지만 스레드를 실행하기위한 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를 사용할 수 있고 이러한 모든 것을 피할 수 있지만, 저는 boost를 사용하지 않도록 C ++ 코드를 수정하려고했습니다 (코드는 단지이 목적을 위해 boost에 대해 링크되었습니다)


1

누군가에게 유용 할 것이라는 희망의 첫 번째 대답 : 이제 이것은 오래된 질문이지만 TcpServer 클래스를 작성하고 pthread를 사용하려고 할 때 위의 질문과 똑같은 오류가 발생했습니다. 나는이 질문을 발견했고 그것이 왜 일어 났는지 이제 이해합니다. 나는 이것을 끝내었다.

#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

// Linux에서 컴파일 한 사용 예제 파일 "test.cc"메서드를 스레드 화하는 모든 작업을 캡슐화하는 클래스 : 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)

그런 다음 p를 보냅니다.

멤버 함수를 사용하여 수행 한 작업도 수행했지만 사용중인 클래스와 정적 함수에서 수행했습니다. 이것이 차이를 만들었다 고 생각합니다.


위의 내용을 시도했지만 구문 오류가 발생했습니다 .. 또한 변경하려고 시도했습니다. pthread_create (...)를 사용하는 것이 도움이 될 수 있음을 보여줄만큼 친절하다면
Angel.King.47

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.