cplus-plus.ru logo
Мы переехали на cplus-plus.ru
Главная страница В закладкиО сайтеКарта сайта
Хостинг от uCoz
Добавить в закладки

Меню сайта

Полезные ссылки

Наша рассылка
Подписаться на рассылку
"C++ : cplus-plus.ru :
Рассылка статей C++"


Друзья сайта
alsproject.ru Выбор выходного разделительного конденсатора

Приветствую Вас, Гость · rss 29-Мар-2024, 17:25
Главная » 2011 » Январь » 9 » Многопоточные классы
12:58
Многопоточные классы

Многопоточные классы
Доброго времени суток. Хочу поведать уважаемому хабрачитателю одну интересную вещь с которой пришлось столкнуться в недавнем времени. В одной из задач, стоявших передо мной, требовалось реализовать класс. В рамках этого класса должны выполняться некоторые вычисления. Для простоты, а также в целях сохранения работоспособности основного кода, решено было использовать потоки. Но все оказалось не так тривиально. 

Первоначальный вариант был таков:

// структура параметров передаваемых в поток
struct ArgsThread
{
int *tmp1;
char *tmp2;
int indata1;
char indata2;
};

// непосредственно поток выполняющий вычисления
unsigned long CalculationThread(void *arg)
{
ThreadArgs* args = reinterpret_cast<ThreadArgs*>(arg); // выкапываем входные данные

// что-то сумбурно и долго считаем
}

// класс который требуется реализовать
class MyCalc
{
private:
void *HandleThread;
unsigned long IdThread;

int tmp1;
char tmp2;

public:
MyCalc(int indata1, char indata2)
{
ArgsThread* args=new ArgsThread();

args->tmp1 = &tmp1; // упаковываем данные
args->tmp2 = &tmp2;
args->indata1 = indata1;
args->indata2 = indata2;

HandleThread = CreateThread(NULL, 0, &CalculationThread, args, 0, &IdThread); // создаем поток
};

~MyCalc
{
TerminateThread(HandleThreade, NULL); // завершаем выполнение потока
CloseHandle(HandleThread); // закрываем хендл
};
};

Поток находился вне класса, что, в принципе, удовлетворяло задаче. После раздумий пришла идея реализации потока внутри класса, а точнее, один из методов должен стать функцией, которую выполнял бы поток.
Недолго думая (что очень зря), родился такой код:

class MyCalc
{
private:
void *HandleThread;
unsigned long IdThread;

int tmp1;
char tmp2;
protected:
unsigned long CalculationThread(void *arg)
{
// что-то сумбурно и долго считаем
}

public:
MyCalc(int indata1, char indata2)
{
HandleThread = CreateThread(NULL, 0, &CalculationThread, this, 0, &IdThread); // создаем поток
};

~MyCalc
{
TerminateThread(HandleThreade, NULL); // завершаем выполнение потока
CloseHandle(HandleThread); // закрываем хендл
};
};

Естественно, во время компиляции я получил ошибку о том, что передаваемые в функцию CreateThread параметры не корректны, а в частности прототип функции потока не соответствовал запрашиваемому типу. На одном из форумов было найдено решение, суть которого заключалась в том, что метод делаем static. Что тоже в принципе удовлетворяло меня, но как оказалось поток не имел доступа ко внтренним данным класса. И в конечном итоге решено было сделать этот метод полноправным членом класса. После полу часа возни с компилятором родилось такое решение:

// объявляем необходимые типы
typedef unsigned long (__stdcall *ThrdFunc)(void *arg); // прототип функции потока
typedef unsigned long (__closure *ClassMethod)(void *arg); // прототип метода класса

// данное объединение позволяет решить несостыковку с типами
typedef union
{
ThrdFunc Function;
ClassMethod Method;
}tThrdAddr;

// для гибкости использования храним все в одном месте
typedef struct
{
void* Handle; // хэндл потока
tThrdAddr Addr; // адрес
unsigned long Id; // ID потока
unsigned long ExitCode; // код выхода
}tThrd;

class MyCalc
{
private:
tThrd MyThread;

protected:
unsigned long ThrdHandle(void *arg)
{
// что-то сумбурно и долго считаем
};

public:
MyCalc()
{
MyThread.Addr.Method = &ThrdHandle; // тут главная магия
MyThread.Handle = CreateThread(NULL, 0, MyThread.Addr.Function, this, 0, &MyThread.Id);
GetExitCodeThread(MyThread.Handle, &MyThread.ExitCode);
};

~MyCalc()
{
if(MyThread.Handle)
{
TerminateThread(MyThread.Handle, MyThread.ExitCode);
CloseHandle(MyThread.Handle);
}
};
};

Таким образом наш поток становится и членом класса одновременно, со всем вытекающими ООП возможностями. Отпадает необходимость в «огороде» из упаковки и распаковки аргументов содержащих данные, поскольку теперь есть доступ ко всем методам и полям класса. Таких методов-потоков можно реализовать бесконечно много, и пользователь класса не сможет получить к ним хоть какой-нибуть доступ (правила private и protected) или случайно вызвать выполнение кода содержащегося в методе-потоке.

P.S. При желании прототип метода может в корне отличатся от того, что объявлен в примере, ведь используется только его адрес, но не стоит забывать и о передаваемых параметрах.

UPD: данный код генерировался и оттачивался в среде C++ Builder, поэтому в прототипе метода присутствует __closure. изменяя прототип вы можете без больших потерь и изменений использовать данный код в других компиляторах.
Источник: http://habrahabr.ru
Категория: Новости | Просмотров: 676 | Добавил: FazaNaka | Рейтинг: 0.0/0
Всего комментариев: 0
Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]