pthread_create - thread c++ ejemplo
pthread funciĆ³n de una clase (8)
Demasiadas veces he encontrado formas de resolver lo que estás pidiendo, en mi opinión son demasiado complicadas. Por ejemplo, tienes que definir nuevos tipos de clases, biblioteca de enlaces, etc. Así que decidí escribir unas líneas de código que permitieran al usuario final básicamente "enhebrar" un "método vacío (método) (vacío)" de cualquier clase. Por supuesto, esta solución que implementé puede ampliarse, mejorarse, etc., así que, si necesita métodos o funciones más específicas, agréguelas y sea tan amable de mantenerme informado.
Aquí hay 3 archivos que muestran lo que hice.
// 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
// La clase que encapsula todo el trabajo para enhebrar un método (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
// Un archivo de ejemplo de uso "test.cc" que en Linux he compilado con La clase que encapsula todo el trabajo para enhebrar un método: 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
Digamos que tengo una clase como
class c {
// ...
void *print(void *){ cout << "Hello"; }
}
Y luego tengo un vector de c
vector<c> classes; pthread_t t1;
classes.push_back(c());
classes.push_back(c());
Ahora, quiero crear un hilo en c.print();
Y lo siguiente me está dando el problema a continuación: pthread_create(&t1, NULL, &c[0].print, NULL);
Error Ouput: no se puede convertir ''void * (tree_item ::) (void )'' en ''void * ( ) (void )'' para el argumento ''3'' en ''int pthread_create (pthread_t *, const pthread_attr_t *, void * ( ) (void ), void *) ''
Las respuestas anteriores son buenas, pero en mi caso, el primer enfoque que convierte la función en estática no funcionó. Estaba tratando de convertir el código de salida para pasar a la función de subproceso, pero ese código ya tenía muchas referencias a miembros de clase no estáticos. La segunda solución de encapsular en objetos de C ++ funciona, pero tiene envoltorios de 3 niveles para ejecutar un hilo.
Tenía una solución alternativa que usa la construcción C ++ existente, la función ''amigo'', y funcionó perfectamente para mi caso. Un ejemplo de cómo utilicé ''amigo'' (utilizará el mismo ejemplo anterior para los nombres que muestran cómo se puede convertir en un formulario compacto usando un amigo)
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;
}
Por supuesto, podemos usar boost :: thread y evitar todo esto, pero estaba tratando de modificar el código C ++ para no usar boost (el código estaba vinculando contra boost solo para este propósito)
Mi forma favorita de manejar un hilo es encapsularlo dentro de un objeto C ++. Aquí hay un ejemplo:
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;
};
Para usarlo, simplemente crearía una subclase de MyThreadClass con el método InternalThreadEntry () implementado para contener el bucle de eventos de su hilo. Necesitarás llamar a WaitForInternalThreadToExit () en el objeto thread antes de eliminar el objeto thread, por supuesto (y tener algún mecanismo para asegurarte de que el hilo realmente sale, de lo contrario WaitForInternalThreadToExit () nunca volvería)
Mi primera respuesta con la esperanza de que sea útil para alguien: ahora esta es una pregunta antigua, pero encontré exactamente el mismo error que la pregunta anterior, ya que estoy escribiendo una clase TcpServer y estaba tratando de usar pthreads. Encontré esta pregunta y ahora entiendo por qué estaba sucediendo. Terminé haciendo esto:
#include <thread>
método para ejecutar threaded -> void* TcpServer::sockethandler(void* lp) {/*code here*/}
y lo llamo con un lambda -> std::thread( [=] { sockethandler((void*)csock); } ).detach();
eso parece un enfoque limpio para mí.
No puede hacerlo de la forma en que lo ha escrito porque las funciones miembro de la clase C ++ tienen un parámetro oculto que pasó. pthread_create()
no tiene idea de qué valor usar, por lo que si trata de evitar el compilador mediante el casting el método para un puntero de función del tipo apropiado, obtendrá una falla de segmentación. Tienes que usar un método de clase estático (que no tiene this
parámetro), o una función ordinaria para iniciar la clase:
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);
Supongo que esto es porque está mutilado un poco por C ++ b / c al enviarle un puntero C ++, no un puntero a la función C. Hay una difference aparentemente. Intenta hacer una
(void)(*p)(void) = ((void) *(void)) &c[0].print; //(check my syntax on that cast)
y luego enviando p.
También hice lo que hace con una función miembro, pero lo hice en la clase que lo estaba usando y con una función estática , que creo que marcó la diferencia.
Tendrás que darle a pthread_create
una función que coincida con la firma que está buscando. Lo que estás pasando no funcionará.
Puede implementar cualquier función estática que desee para hacer esto, y puede hacer referencia a una instancia de c
y ejecutar lo que desee en el hilo. pthread_create
está diseñado para tomar no solo un puntero de función, sino un puntero a "contexto". En este caso, solo le pasa un puntero a una instancia de c
.
Por ejemplo:
static void* execute_print(void* ctx) {
c* cptr = (c*)ctx;
cptr->print();
return NULL;
}
void func() {
...
pthread_create(&t1, NULL, execute_print, &c[0]);
...
}
C ++: ¿Cómo pasar la función de miembro de clase a pthread_create ()?
http://thispointer.com/c-how-to-pass-class-member-function-to-pthread_create/
typedef void * (*THREADFUNCPTR)(void *);
class C {
// ...
void *print(void *) { cout << "Hello"; }
}
pthread_create(&threadId, NULL, (THREADFUNCPTR) &C::print, NULL);