hàm pthread từ một lớp


86

Giả sử tôi có một lớp học chẳng hạn như

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

Và sau đó tôi có một vectơ c

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

Bây giờ, tôi muốn tạo một chuỗi trên c.print();

Và sau đây là cho tôi vấn đề dưới đây: pthread_create(&t1, NULL, &c[0].print, NULL);

Lỗi Ouput: không thể chuyển đổi 'void * (tree_item : :) (void )' thành 'void * ( ) (void )' cho đối số '3' thành 'int pthread_create (pthread_t *, const pthread_attr_t *, void * ( ) (void ), void *) '

Câu trả lời:


147

Bạn không thể làm điều đó theo cách bạn đã viết vì các hàm thành viên lớp C ++ có một thistham số ẩn được truyền vào. pthread_create()Không có ý tưởng về giá trị nào thisđể sử dụng, vì vậy nếu bạn cố gắng sử dụng trình biên dịch bằng cách truyền phương thức đến một hàm con trỏ của loại thích hợp, bạn sẽ gặp lỗi phân tách. Bạn phải sử dụng một phương thức lớp tĩnh (không có thistham số) hoặc một hàm thông thường đơn giản để khởi động lớp:

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

ở trên sẽ hoạt động với vectơ theo cách sau: pthread_create (& t, NULL, & C :: hello_helper, & vector_c [0]); ?
Angel.King. 47

Tất cả các nhận xét trên đều hữu ích, tôi đã sử dụng kết hợp tất cả để giải quyết một vấn đề .. Nó vẫn liều lĩnh như tôi đã cố gắng làm điều đó ... Nhưng rất tiếc, tôi chỉ có thể đánh dấu một là đúng, nếu không thì mọi người đều nhận tín dụng..Cảm ơn
Angel.King. 47

Tôi muốn tán thành câu trả lời này, nhưng nó sử dụng phôi kiểu C, phải được chấm dứt với định kiến ​​cực đoan. Câu trả lời này là chính xác.
Chris Jester-Young

@Chris: Tôi không muốn tham gia vào một cuộc thánh chiến về kiểu diễn viên, nhưng sử dụng kiểu diễn viên kiểu C trong trường hợp này là hoàn toàn chính xác về mặt ngữ nghĩa.
Adam Rosenfield

2
@AdamRosenfield việc xâu chuỗi các trạng từ lại với nhau cũng hoàn toàn chính xác về mặt ngữ nghĩa, nhưng điều đó không làm cho nó trở nên tốt! xD
ACK_stoverflow

82

Cách yêu thích của tôi để xử lý một luồng là đóng gói nó bên trong một đối tượng C ++. Đây là một ví dụ:

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

Để sử dụng nó, bạn chỉ cần tạo một lớp con của MyThreadClass với phương thức InternalThreadEntry () được triển khai để chứa vòng lặp sự kiện của luồng của bạn. Tất nhiên, bạn cần phải gọi WaitForInternalThreadToExit () trên đối tượng luồng trước khi xóa đối tượng luồng (và có một số cơ chế để đảm bảo luồng thực sự thoát, nếu không WaitForInternalThreadToExit () sẽ không bao giờ trở lại)


1
Đó là một cách tuyệt vời để tôi có thể hiểu được việc sử dụng Lớp ảo ở trên, Nhưng tôi gặp nhiều vấn đề khó khăn hơn..Tôi có các luồng sinh ra từ các luồng khác cần được đặt tất cả trong một vectơ. Và sau đó là một vòng lặp đệ quy để đi và nối tất cả các chuỗi. Im chắc chắn tôi có thể thực hiện ở trên để làm điều đó cũng bằng cách gọi chờ đợi ở nơi thích hợp, Nhưng il thử nó để xem nơi tôi nhận được đến
Angel.King.47

4
Giải pháp này rất thanh lịch. Tôi sẽ sử dụng nó từ bây giờ. Cảm ơn Jeremy Friesner. +1
Armada

xin chào Jeremy Friesner, làm thế nào để chuyển một tham chiếu đến InternalThreadEntry (aclass_ref & refobj)? tôi nên thực hiện những thay đổi nào?
sree

@sree Thêm tham chiếu (hoặc một con trỏ) vào MyThreadClass như một biến thành viên; thì InternalThreadEntry () có thể truy cập trực tiếp mà không cần phải lo lắng về việc truyền nó qua đối số (void *).
Jeremy Friesner

10

Bạn sẽ phải cung cấp pthread_createmột hàm phù hợp với chữ ký mà nó đang tìm kiếm. Những gì bạn đang vượt qua sẽ không hoạt động.

Bạn có thể triển khai bất kỳ hàm tĩnh nào bạn muốn để thực hiện việc này và nó có thể tham chiếu đến một phiên bản cvà thực thi những gì bạn muốn trong chuỗi. pthread_createđược thiết kế để không chỉ nhận một con trỏ hàm, mà còn một con trỏ tới "ngữ cảnh". Trong trường hợp này, bạn chỉ cần chuyển nó một con trỏ đến một thể hiện của c.

Ví dụ:

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 tôi xem những gì bạn có nghĩa là .. vượt qua nó con trỏ của c, Gotcha .. sẽ thực hiện và thử nó ra
Angel.King.47

2

Các câu trả lời ở trên là tốt, nhưng trong trường hợp của tôi, cách tiếp cận thứ nhất chuyển đổi hàm thành tĩnh không hoạt động. Tôi đã cố gắng chuyển đổi mã thoát để chuyển sang hàm luồng nhưng mã đó đã có rất nhiều tham chiếu đến các thành viên lớp không tĩnh rồi. Giải pháp thứ hai của việc đóng gói vào đối tượng C ++ hoạt động, nhưng có trình bao bọc 3 cấp để chạy một luồng.

Tôi đã có một giải pháp thay thế sử dụng cấu trúc C ++ hiện có - hàm 'bạn bè' và nó hoạt động hoàn hảo cho trường hợp của tôi. Một ví dụ về cách tôi sử dụng 'friend' (sẽ sử dụng cùng một ví dụ ở trên cho các tên cho thấy cách nó có thể được chuyển đổi thành một dạng thu gọn bằng cách sử dụng friend)

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

Tất nhiên, chúng ta có thể sử dụng boost :: thread và tránh tất cả những điều này, nhưng tôi đã cố gắng sửa đổi mã C ++ để không sử dụng boost (mã liên kết với boost chỉ cho mục đích này)


1

Câu trả lời đầu tiên của tôi với hy vọng rằng nó sẽ hữu ích cho ai đó: Tôi bây giờ đây là một câu hỏi cũ nhưng tôi gặp chính xác lỗi giống như câu hỏi ở trên khi tôi đang viết một lớp TcpServer và tôi đang cố gắng sử dụng pthreads. Tôi đã tìm thấy câu hỏi này và giờ tôi đã hiểu tại sao nó lại xảy ra. Tôi đã kết thúc việc này:

#include <thread>

phương thức để chạy luồng -> void* TcpServer::sockethandler(void* lp) {/*code here*/}

và tôi gọi nó bằng lambda -> std::thread( [=] { sockethandler((void*)csock); } ).detach();

đó có vẻ là một cách tiếp cận rõ ràng đối với tôi.


0

Đã quá nhiều lần tôi tìm cách giải quyết những gì bạn đang yêu cầu, theo tôi là quá phức tạp. Ví dụ, bạn phải xác định các loại lớp mới, thư viện liên kết, v.v. Vì vậy, tôi quyết định viết một vài dòng mã cho phép người dùng cuối về cơ bản có thể "luồng-ize" một "void :: method (void)" của bất cứ lớp nào. Để chắc chắn rằng giải pháp này mà tôi đã triển khai có thể được mở rộng, cải tiến, v.v., vì vậy, nếu bạn cần các phương pháp hoặc tính năng cụ thể hơn, hãy thêm chúng và vui lòng thông báo cho tôi.

Đây là 3 tệp cho thấy những gì tôi đã làm.

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

// Lớp đóng gói tất cả công việc để luồng-ize một phương thức (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

// Một tệp ví dụ sử dụng "test.cc" trên linux mà tôi đã biên dịch với Lớp đóng gói tất cả công việc để luồng-ize một phương thức: 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

Đây là một câu hỏi hơi cũ nhưng là một vấn đề rất phổ biến mà nhiều người phải đối mặt. Sau đây là một cách đơn giản và thanh lịch để xử lý điều này bằng cách sử dụng 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';
}

Đoạn mã trên cũng xử lý việc truyền đối số cho hàm luồng.

Tham khảo tài liệu std :: thread để biết thêm chi tiết.


-1

Dự đoán của tôi sẽ là điều này b / c của nó bị xáo trộn một chút bởi C ++ b / c của bạn gửi cho nó một con trỏ C ++, không phải một con trỏ hàm C. Có một sự khác biệt rõ ràng. Hãy thử làm một

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

và sau đó gửi p.

Tôi cũng đã làm những gì bạn đang làm với một hàm thành viên, nhưng tôi đã làm điều đó trong lớp đang sử dụng nó và với một hàm tĩnh - điều mà tôi nghĩ đã tạo ra sự khác biệt.


Tôi đã cố gắng trên nhưng cho nó lỗi tôi cú pháp .. Cố gắng thay đổi nó xung quanh cũng ... Nếu bạn sẽ có đủ loại để chứng minh rằng việc sử dụng pthread_create (...) nó có thể là hữu ích
Angel.King.47

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.