06 kwi, 2010
Uruchamianie metod klas w nowych wątkach w c++ i pthreads
Zamieszczony przez: pejotr w: C++|Programowanie
Podczas pisania kolejnego projektu w języku c++ natknąłem się na interesujący problem. Program docelowo miał działać na systemie czasu rzeczywistego zatem jego celem było również zapoznanie z różnymi mechanizmami RTOS. Pierwszy problem pojawił się przy korzystaniu z mechanizmu wielowątkowości, pewnie byśmy się nie dowiedzieli o jego istnieniu gdybyśmy nie zdecydowali się na programowanie obiektowe, ale po kolei…
Każdy kto programował w C++ korzystając z biblioteki Boost::Threads napewno wie że aby uruchomić metodę obiektu w nowym wątku wystarczy przeciążyć operator () i każdy obiekt takiej klasy zaczyna z powodzeniem udawać funkcję. Dzięki temu można taki obiekt przekazać do konstruktora boost::thread. Poniższy przykład obrazuje taką sposób działania:
// Copyright (C) 2001-2003
// William E. Kempf
//
// Permission to use, copy, modify, distribute and sell this software
// and its documentation for any purpose is hereby granted without fee,
// provided that the above copyright notice appear in all copies and
// that both that copyright notice and this permission notice appear
// in supporting documentation. William E. Kempf makes no representations
// about the suitability of this software for any purpose.
// It is provided "as is" without express or implied warranty.
#include <boost/thread/thread.hpp>;
#include <;boost/thread/xtime.hpp>;
#include <iostream>;
struct thread_alarm
{
thread_alarm(int secs) : m_secs(secs) { }
void operator()()
{
boost::xtime xt;
boost::xtime_get(&xt, boost::TIME_UTC);
xt.sec += m_secs;
boost::thread::sleep(xt);
std::cout << "alarm sounded..." << std::endl;
}
int m_secs;
};
int main(int argc, char* argv[])
{
int secs = 5;
std::cout << "setting alarm for 5 seconds..." << std::endl;
thread_alarm alarm(secs);
boost::thread thrd(alarm);
thrd.join();
}
Jednak ten sposób nie działa w przypadku korzystania z natywnego mechanizmu wielowątkowości dostępnego w każdym systemie *nix. Biblioteka pthreads nie radzi sobie z wywołaniem przeciążonego operatora (). Problem trzeba było rozwiązać jak najszybciej, żeby projekt mógł ruszy dalej. Dlatego powstał ten sprytny „work-around” umożliwiający uruchomienie dowolnej metody obiektu w nowym wątku. Jest to właściwie funkcja proxy, takie proste a ułatwia życie:
template<class T>
struct ThreadParams
{
T *obj;
void *params;
};
template<class T, void* (T::*method)(void*)>
void* run_thread(void *params)
{
ThreadParams<T> *t = static_cast<ThreadParams><T*>(params);
T* obj = static_cast<T*>(t->obj);
(obj->*method)(t->params);
return NULL;
}
Dodatkowy wkład pracy jaki należy włożyć aby wykorzystać powyższy kod ogranicza się właściwie tylko do wypełnienia struktury ThreadParams:
class Test
{
public:
void* fun(void* params) {
printf("Hello World! \n");
printf("Params: %d\n", reinterpret_cast<int>(params));
pthread_exit(NULL);
}
};
int main() {
pthread_t tid;
void* status;
Test test;
ThreadParams<Test> params;
params.obj = &test;
params.params = reinterpret_cast<void>(123);
pthread_create(&tid, NULL, run_thread<Test, &Test::fun>, (void*)(¶ms));
pthread_join(tid, &status);
}
Witam, nazywam się Piotr Doniec, w internecie występuję pod nickami 'pejotr' oraz 'doniczek'. Obecnie jestem studentem 3 roku informatyki na Politechnice Warszawskiej na wydziale Elektroniki i Technik Informacyjnych.