Обновить

Комментарии 37

Спасибо. Когда то давно с этим же сталкивался.
Можно было, конечно, пойти по пути наследования какого-нибудь готового класса из таких библиотек как: VCL (классTThread) иди Qt (классQThread), но тогда мы бы ограничивались лишь реализацией одного единственного потока в рамках данного класса. А тут мы получаем возможность реализовывать многопоточные классы, которые с легкостью переносятся на другие платформы.
Свою проблему я как раз решил переходом на Qt.
Стоило бы упомянуть что __closure – расширение языка в реализации C++ Builder, подобное лихачество непереносимо и небезопасно.
Полностью с вами согласен, поскольку данный код оттачивался в среде 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 в аргументе.
мой способ не претендует на оригинальность. вы всегда можете воспользоваться другими или привести прототип метода к необходимому типу, это ничуть не усложнит и почти не изменит код.
Тут уже писали — использовать __closure — это читерство! :) Существует стандартный метод — попробуйте использовать такое колдунство, как указатель на метод класса, тогда код станет собираться и работать с любым компилятором.
как уже говорилось выше и в топике, вы можете изменять прототип метода как вам угодно. я лишь показал один из возможных примеров реализации.
Ну зачем везде городить ифдефы, если можно сразу всё написать по-человечески?
в чем же тогда заключается не человечность примера из топика, и где вы увидели в нем использование препроцессора?
Ну приведите пример без __closure для Visual C++
не имею под рукой данного компилятора, но думаю это будет работать:
typedef unsigned long (ClassName:: *ClassMethod)(void *arg); // прототип метода класса ClassName
А препроцессоры придется ставить именно для определения компилятора например.
а зачем его определять, например, если разработка ведется в конкретной среде с, вполне, явным компилятором, к чему тогда использовать препроцессор для определения его типа, или вы при написании кода одновременно используете множество компиляторов? согласен что его использование возможно для обеспечения кроссплатформенности, но в рабочем проекте, который уже отлажен и написан под вполне конкретные условия, не вижу смысла добиваться кросскомпиляции.
НЛО прилетело и опубликовало эту надпись здесь
Пожалуй, это стоит перенести в блог «Ненормальное программирование».
НЛО прилетело и опубликовало эту надпись здесь
с boost::thread я банально не очень хорошо знаком. а beginthread(ex) не использую потому, что пример тривиальный и лишь для наглядности был приведен, но также и по причине того, что в потоке не используются вызовы CRT, в остальном согласен — лучше использовать beginthread(ex).
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
Согласен. Всё это вывглядит как кастыли. Но он молодец, что написал статью на хабр. Главное, чтобы он всю критику нормально воспринял. Эта статья с комментариями довольно поучительна!
с костылем соглашусь. но ведь это работает. и вполне стабильно. просто на момент написания данного фрагмента это решение было одним из явных и реализуемых на мой взгляд. кто-то решил бы по другому и интересно было бы взглянуть как.
а что не так в них, и вообще во всем что в топике, почему сразу бред? можно увидеть вполне конкретные указания на наличие ошибок или тех мест где бред, или других возможных вариантов решения задачи? интересно было бы прочитать и обоснование ваших выводов.
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
спасибо за критику. признаю ошибки. про TerminateThread даже не догадывался о возможных последствиях. но тогда к вам вопрос. как же корректно поступить с завершением и уничтожением потока в данном случае?
НЛО прилетело и опубликовало эту надпись здесь
Наверное это можно было бы реализовать следующим образом, только зачем?..
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;
};
или даже вот так:
template<typename T, DWORD(__thiscall T::* pMethod)()>
DWORD WINAPI Function(PVOID pParam)
{
    return ((T*)pParam->*pMethod)();
};
...
CreateThread(NULL, 0, Function<MyCalc, &MyCalc::ThrdHandle>, this, 0, NULL);
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации