Comments 37
Спасибо. Когда то давно с этим же сталкивался.
Можно было, конечно, пойти по пути наследования какого-нибудь готового класса из таких библиотек как: VCL (классTThread) иди Qt (классQThread), но тогда мы бы ограничивались лишь реализацией одного единственного потока в рамках данного класса. А тут мы получаем возможность реализовывать многопоточные классы, которые с легкостью переносятся на другие платформы.
Стоило бы упомянуть что __closure – расширение языка в реализации C++ Builder, подобное лихачество непереносимо и небезопасно.
а не было бы проще это сделать как-то так?
class MyCalc
{
private:
void *HandleThread;
unsigned long IdThread;
int tmp1;
char tmp2;
static DWORD WINAPI ThreadF(void *arg)
{
return ((MyCalc*)arg)->CalculationThread();
}
protected:
unsigned long CalculationThread()
{
/////////
}
public:
MyCalc(int indata1, char indata2)
{
HandleThread = CreateThread(NULL, 0, ThreadF, this, 0, &IdThread);
};
~MyCalc()
{
TerminateThread(HandleThread, NULL);
CloseHandle(HandleThread);
};
};
ваше решение абсолютно справедливо. в своем примере я лишь хотел показать, как сделать потоком метод класса, со всеми плюшками ООП. ваш случай не защищен от вызова функции ThreadF только классом. к тому же она не является его членом, хоть и описана внутри.
если ThreadF была бы friend, то да, можно было бы вызвать её из вне класса. в моём примере она является static и находится в блоке private, что как раз и защищает её от вызова из вне и сохраняет её принадлежность классу. по крайней мере в Visual Studio это работет по такому принципу
в C++ Builder ваш пример потребовал необходимого преобразования типов, или же объявления ThreadF как __stdcall. также в вашем случае вы не имеете полного доступа к данным класса непосредственно из потока без указания пренадлежности к объекту класса, поэтому вынужденны делать дополнительный метод. в моем примере достаточно лишь выполнить необходимое преобразование типов для адресов метода и функции, после чего метод становится потоком и может получать беспрепятственный доступ данным класса. к тому же при использовании static при динамическом создании и удалении экземпляра класса есть вероятность, что метод ThreadF останется в памяти, что может грозить некоторыми ее утечками.
__closure — если бы он еще всеми компиляторами поддерживался бы.
Так что да, лучше не занимать любовью себе мозги и передавать указатель на this в аргументе.
Так что да, лучше не занимать любовью себе мозги и передавать указатель на this в аргументе.
Тут уже писали — использовать __closure — это читерство! :) Существует стандартный метод — попробуйте использовать такое колдунство, как указатель на метод класса, тогда код станет собираться и работать с любым компилятором.
как уже говорилось выше и в топике, вы можете изменять прототип метода как вам угодно. я лишь показал один из возможных примеров реализации.
Ну зачем везде городить ифдефы, если можно сразу всё написать по-человечески?
в чем же тогда заключается не человечность примера из топика, и где вы увидели в нем использование препроцессора?
Ну приведите пример без __closure для Visual C++
А препроцессоры придется ставить именно для определения компилятора например.
а зачем его определять, например, если разработка ведется в конкретной среде с, вполне, явным компилятором, к чему тогда использовать препроцессор для определения его типа, или вы при написании кода одновременно используете множество компиляторов? согласен что его использование возможно для обеспечения кроссплатформенности, но в рабочем проекте, который уже отлажен и написан под вполне конкретные условия, не вижу смысла добиваться кросскомпиляции.
>а зачем его определять, например, если разработка ведется в конкретной среде с, вполне, явным компилятором
Так говорят только программисты-неудачники. Увы, но во всех книжках, на всех учебных курсах говорят о том, что нужно писать масштабируемый и переносимый код, чтобы не иметь геморроя в будущем.
Так говорят только программисты-неудачники. Увы, но во всех книжках, на всех учебных курсах говорят о том, что нужно писать масштабируемый и переносимый код, чтобы не иметь геморроя в будущем.
Пожалуй, это стоит перенести в блог «Ненормальное программирование».
Бред какой-то
>UPD: данный код генерировался и оттачивался в среде C++ Builder, поэтому в прототипе метода присутствует __closure. изменяя прототип вы можете без больших потерь и изменений использовать данный код в других компиляторах.
Если бы я увидел такое в коде коллеги, послал бы его переписывать всё нафиг.
>UPD: данный код генерировался и оттачивался в среде C++ Builder, поэтому в прототипе метода присутствует __closure. изменяя прототип вы можете без больших потерь и изменений использовать данный код в других компиляторах.
Если бы я увидел такое в коде коллеги, послал бы его переписывать всё нафиг.
за union'ы впрочем тоже.
Согласен. Всё это вывглядит как кастыли. Но он молодец, что написал статью на хабр. Главное, чтобы он всю критику нормально воспринял. Эта статья с комментариями довольно поучительна!
а что не так в них, и вообще во всем что в топике, почему сразу бред? можно увидеть вполне конкретные указания на наличие ошибок или тех мест где бред, или других возможных вариантов решения задачи? интересно было бы прочитать и обоснование ваших выводов.
Наверное это можно было бы реализовать следующим образом, только зачем?..
template<typename T>
struct tThrd
{
typedef DWORD(__thiscall T::* PMethod)();
static DWORD WINAPI Function(PVOID pParam)
{
return (((tThrd*)pParam)->pThis->*((tThrd*)pParam)->pMethod)();
};
T* pThis;
PMethod pMethod;
HANDLE Handle;
};
class MyCalc
{
public:
MyCalc()
{
// здесь нет никакой магии :)
MyThread.pThis = this;
MyThread.pMethod = &MyCalc::ThrdHandle;
MyThread.Handle = CreateThread(NULL, 0, MyThread.Function, &MyThread, 0, NULL);
}
~MyCalc()
{
WaitForSingleObject(MyThread.Handle, INFINITE);
CloseHandle(MyThread.Handle);
}
public:
DWORD ThrdHandle()
{
// что-то сумбурно и долго считаем
Sleep(10000);
return 1;
}
private:
tThrd<MyCalc> MyThread;
};
Sign up to leave a comment.
Многопоточные классы