CTAPOBEP
|
|
« : 17-10-2008 09:10 » |
|
Именно. Пользуюсь сам этой штукой уже какое-то время, проблем никаких не было - может еще кому пригодится. Thread.h class C_Thread { CWinThread *m_pThread; public: C_Thread(void); virtual ~C_Thread(void); CWinThread *operator ->(void); void CreateThread(AFX_THREADPROC pfnThreadProc, LPVOID pParam); };
#define THREAD(thread_function, class_name) \ C_Thread m_Thread##thread_function;\ void Start##thread_function(void)\ {\ m_Thread##thread_function.CreateThread(ThreadFunction##thread_function, (void*)this);\ }\ static UINT __cdecl ThreadFunction##thread_function(LPVOID pParam)\ {\ static bool run=false;\ ##class_name *dlg=(##class_name*)pParam;\ if (pParam != NULL && !run)\ {\ run=true;\ dlg->##thread_function();\ run=false;\ }\ AfxEndThread(0);\ return 0;\ }
Thread.cpp C_Thread::C_Thread(void) { m_pThread=NULL; }
C_Thread::~C_Thread(void) { }
CWinThread *C_Thread::operator ->(void) { if (!m_pThread) { AfxMessageBox("Попытка вызвать функцию для незапущенного потока"); m_pThread=new CWinThread; } return m_pThread; }
void C_Thread::CreateThread(AFX_THREADPROC pfnThreadProc, LPVOID pParam) { m_pThread=AfxBeginThread(pfnThreadProc, pParam); m_pThread->m_bAutoDelete=true; }
Пользоваться так: #include "Thread.h"
class CTestThreadDlg : public CDialog { // Объявляем функцию первого потока, пусть она просто бипает раз в секунду. // Это обычная функция класса, поэтому она имеет доступ ко всем членам класса. // (для простоты реализацию функции привожу здесь же) void Beep1(void) { while (true) { MessageBeep(MB_OK); Sleep(1000); } }
// Объявляем первый поток THREAD(Beep1, CTestThreadDlg); // здесь Beep1 - имя функции потока, // CTestThreadDlg - имя текущего класса
// Объявляем функцию второго потока, пусть она тоже просто бипает раз в секунду void Beep2(void) { while (true) { MessageBeep(MB_ICONEXCLAMATION); Sleep(1000); } }
// Объявляем второй поток THREAD(Beep2, CTestThreadDlg); ...
BOOL CTestThreadDlg::OnInitDialog() { CDialog::OnInitDialog();
// Запускаем оба потока StartBeep1(); Sleep(500); // Рассинхронизация, чтобы бипы не накладывались StartBeep2(); ...
Вуаля!!! Несколько замечаний: 1. Чтобы приостановить первый поток из примера, использовать команду m_ThreadBeep1->SuspendThread(); Аналогично вызываются и другие функции класса CWinThread. 2. Поскольку в макросе используется указатель this на объект текущего класса, нельзя запускать поток в конструкторе (там this еще не определен). 3. Синхронизацию потоков, организацию общего доступа к данным и т.д. никто не отменял. Замечание 4-е и все последующие могут добавить сюда все желающие. И еще просьба - не надо, шерсть на носу, задавать вопрос "зачем?", я все равно не знаю на него ответа :ф)
|
|
|
Записан
|
|
|
|
Вад
|
|
« Ответ #1 : 17-10-2008 09:22 » |
|
Я не буду задавать вопрос "Зачем". Мне интересно, почему - макросы Объявление сущности потока - нечитабельное, пункт 1 приводит к появлению загадочного кода. Человек, сопровождающий твой код, тебя проклянёт Может, стоило всё-таки шаблоном?
|
|
« Последнее редактирование: 17-10-2008 10:19 от Вад »
|
Записан
|
|
|
|
Алексей++
глобальный и пушистый
Глобальный модератор
Offline
Сообщений: 13
|
|
« Ответ #2 : 17-10-2008 09:24 » |
|
как бы тебе ни хотелось Зачем? У меня и без дополнительного класса всё неплохо получается
|
|
|
Записан
|
|
|
|
zubr
Гость
|
|
« Ответ #3 : 17-10-2008 10:51 » |
|
Я бы к данному классу добавил методы: Terminate - завершение потока WaitFor - ожидание завершения потока переменную Terminated - обозначающую состояние потока. К примеру в цикле потока можно было бы писать: while (! Terminated) { ....... }
|
|
|
Записан
|
|
|
|
CTAPOBEP
|
|
« Ответ #4 : 18-10-2008 06:42 » |
|
Объявление сущности потока - нечитабельное, пункт 1 приводит к появлению загадочного кода. Человек, сопровождающий твой код, тебя проклянёт
Лишний повод поупражняться в написании комментариев. Любите ли вы писать комментарии, как люблю писать их я? Впрочем, насчет шаблона - что-то тут есть... У меня и без дополнительного класса всё неплохо получается
А у меня - нет. Ну не поднимается у меня рука объявить глобальную функцию. Не любите ли вы объявлять глобальные функции, как любит их объявлять не я? Я бы к данному классу добавил методы: Terminate - завершение потока WaitFor - ожидание завершения потока переменную Terminated - обозначающую состояние потока. К примеру в цикле потока можно было бы писать: while (! Terminated) { ....... }
Функция потока в данном случае - обычная функция класса, она имеет доступ ко всем членам класса, в котором объявлена, поэтому все это организуется элементарно. Собственно, макрос был написан просто для удобства объявления потока.
|
|
« Последнее редактирование: 18-10-2008 06:56 от CTAPOBEP »
|
Записан
|
|
|
|
Алексей++
глобальный и пушистый
Глобальный модератор
Offline
Сообщений: 13
|
|
« Ответ #5 : 18-10-2008 07:01 » |
|
CTAPOBEP, А у меня - нет. Ну не поднимается у меня рука объявить глобальную функцию. Не любите ли вы объявлять глобальные функции, как любит их объявлять не я?
никогда не делал глобальной Беру в любом удобном мне классе, пишу статическую static DWORD F(void*); -и готово. Функция потока в данном случае - обычная функция класса, она имеет доступ ко всем членам класса, в котором объявлена, поэтому все это организуется элементарно. Собственно, макрос был написан просто для удобства объявления потока.
А доступ ко всем членам класса - оно зачем такая радость ? Передай, что нужно, через указатели и всё. А отлаживать макрос затрахолебаешься Писал я раньше большие макросы, теперь предпочитаю их функцией оформлять просто потому, что отлаживать удобнее
|
|
« Последнее редактирование: 18-10-2008 09:28 от Алексей1153++ »
|
Записан
|
|
|
|
Вад
|
|
« Ответ #6 : 18-10-2008 07:59 » |
|
Объявление сущности потока - нечитабельное, пункт 1 приводит к появлению загадочного кода. Человек, сопровождающий твой код, тебя проклянёт
Лишний повод поупражняться в написании комментариев. Любите ли вы писать комментарии, как люблю писать их я? Intel точно не любит писать комментарии Дело в том, что я над подобными комбинациями по конструированию имён функций и глобальных переменных с помощью макросов во время компиляции, в том числе и по месту их вызова, медитировал довольно много времени, пытаясь понять, как интеловская OpenCV использует плагины вместо стандартных реализаций функций Но там авторы, видимо, не определились, на чём они пишут - C или C++ Вообще, ход мысли мне немного напоминает реализацию потоков на Java - там, сколь мне помнится, есть интерфейс Runnable, который содержит публичный метод run. Когда создаёшь поток, можешь передать ему Runnable в конструктор, и в качестве своей функции он запустит именно Runnable.run(). По идее, C++ позволяет делать подобные вещи, и это смотрелось бы более логично, чем вместо интерфейса генерировать методы прямо в классе, где требуется поток. Разница только в том, что может потребоваться более одного потока. То есть, в общем-то, нужна только возможность параметризации вызываемой функции - для таких более сложных случаев завязка на интерфейс уже не помогает, и надо как-то реализовать конкретно параметризацию. Имхо, проще и нагляднее это сделать предъявлением к функции определённых требований (соответствия типу) и передачи указателя на неё явно в качестве параметра при старте потока. Но можно подумать и над более изящным решением Впрочем, всегда можно сказать, что более одного потока, непосредственно принадлежащего одной сущности - это дефект проектирования
|
|
|
Записан
|
|
|
|
Алексей++
глобальный и пушистый
Глобальный модератор
Offline
Сообщений: 13
|
|
« Ответ #7 : 18-10-2008 08:20 » |
|
Впрочем, всегда можно сказать, что более одного потока, непосредственно принадлежащего одной сущности - это дефект проектирования
это почему вдруг ? )
|
|
|
Записан
|
|
|
|
CTAPOBEP
|
|
« Ответ #8 : 18-10-2008 09:22 » |
|
Беру в любом удобном мне классе, пишу статическую static DWORD F(void*); -и готово. Передай, что нужно, через указатели и всё.
Макрос как раз для автоматизации всего этого. теперь предпочитаю их йункцией оформлять просто потому, что отлаживать удобнее
В случае использования нескольких потоков для каждого потока нужна своя функция, поэтому макрос и понадобился. Кстати, по той же причине и шаблон тут не получается.
|
|
|
Записан
|
|
|
|
Алексей++
глобальный и пушистый
Глобальный модератор
Offline
Сообщений: 13
|
|
« Ответ #9 : 18-10-2008 09:31 » |
|
В случае использования нескольких потоков для каждого потока нужна своя функция
ой, ты чО У меня одна функция, а потоков из неё можно сделать хоть 100. А если код потоков должен быть разный - то макрос сложно станет отлаживать. Насчёт шаблона не знаю - не пользуюсь (не признаЮ вернее )
|
|
|
Записан
|
|
|
|
Вад
|
|
« Ответ #10 : 18-10-2008 14:00 » |
|
Впрочем, всегда можно сказать, что более одного потока, непосредственно принадлежащего одной сущности - это дефект проектирования
это почему вдруг ? ) Потому что у тебя выходит сущность, которая сама, непосредственно, выполняет разнородные действия в двух параллельных потоках. Скорее всего, такая сущность несёт две разных функции (или зачем-то непосредственно поддерживает исполнение двух одинаковых функций параллельно). А это уже похоже на проблемы декомпозиции
|
|
|
Записан
|
|
|
|
Алексей++
глобальный и пушистый
Глобальный модератор
Offline
Сообщений: 13
|
|
« Ответ #11 : 18-10-2008 14:54 » |
|
Вад, не могу понять, на пальцах покажи
|
|
|
Записан
|
|
|
|
Вад
|
|
« Ответ #12 : 18-10-2008 21:25 » |
|
Если на пальцах, то зачем нужны потоки? Чтобы обрабатывать поступающую информацию и реагировать асинхронно, в фоне и не мешая всем прочим.
Вариант первый: когда у тебя два потока - одинаковые. То есть, они полностью идентичны по выполняемым функциям, и только данные, которые они обрабатывают, различаются по значению (но одинаковые по типу). Тогда налицо два канала обработки данных. Возникает вопрос: а для чего они два (и более) являются непосредственными и неотделимыми частями общего класса? Как порождаются эти каналы? Как взаимодействуют между собой? Имхо, довольно стандартное решение - инкапсулировать такой канал в виде отдельного класса, и при необходимости иметь некий менеджер каналов, который ни сном ни духом об их логике работы, а только управляет количеством.
Вариант второй - когда обрабатываются различные данные и по различным принципам. Тут, имхо, всё ещё проще: наличие у сущности двух различных функций - симптом неверной декомпозиции, нарушение принципа "одна сущность - одна функция" в том смысле, что сложную разнородную логику лучше разделять на более простые составные части хотя бы из соображений внятности и стройности архитектуры. Поток - это признак тяжеловесной обработки или специфической асинхронной реакции. Две различных реакции - это уже как-то странно. Наверное, всегда можно упростить логику её дальнейшей декомпозицией.
|
|
|
Записан
|
|
|
|
Антон (LogRus)
|
|
« Ответ #13 : 20-10-2008 05:53 » |
|
std::mem_fun boost::function std::bind1st boost::bind boost::thread boost:thread_group boost::thread_specific_ptr - это мой выбор макросы и статики идут лесом http://www.boost.org/doc/libs/1_36_0/doc/html/thread/thread_management.htmlclass A { public : void TreadWorker(long, int, const char *); void StopThread(); };
int main() { A a; boost::thread thrd(boost::bind(A::ThreadWorker, &a , 12, 14, "Hi!"));
boost::tread_group thrdGrp; for (size_t i = 0; i != 10 ; ++i) thdrGrp.create_thread(boost::bind(A::ThreadWorker, &a , 12 + i, 14, "Hi!")); .................................... a.StopThread(); // можно заменить на специальные средства остановки реализованные в boost thrd.join(); thrdGrp.join_all(); return 0; }
Вад, поддерживаю, кстати в первом случае иногда удобней, что бы менеджер каналов не только плодил каналы, но и потоки т.е. управляя соотношением многие ко многим, ИМХО не всегда канал должен управлять нитью иногда, удобней делать асинхронную обёртку над синхронным каналом.
|
|
|
Записан
|
Странно всё это....
|
|
|
CTAPOBEP
|
|
« Ответ #14 : 14-11-2008 05:28 » |
|
Шаблон до кучи. thread.h template <class class_name> class C_Thread4 { typedef void (class_name::*FUNCTION_NAME)(void); static FUNCTION_NAME m_Function;
CWinThread *m_pThread;
static UINT __cdecl ThreadFunction(LPVOID pParam) { (((class_name*)pParam)->*m_Function)();
AfxEndThread(0); return 0; }
public: C_Thread4(void) {} virtual ~C_Thread4(void) {} void Start(class_name *pObject, FUNCTION_NAME function) { m_Function=function;
if (!pObject) return; m_pThread=AfxBeginThread(ThreadFunction, pObject); m_pThread->m_bAutoDelete=true; } CWinThread* pCWinThread(void) const { if (!m_pThread) { AfxMessageBox("Попытка выполнить функцию для незапущенного потока"); m_pThread=new CWinThread; } return m_pThread; } };
template <class class_name> typedef void (class_name::*FUNCTION_NAME)(void);
template <class class_name> FUNCTION_NAME C_Thread4<class_name>::m_Function;
Пример использования: #include "thread.h"
class CTestThreadDlg : public CDialog { // Объявляем потоки (<CTestThreadDlg> - это <Имя_текущего_класса>) C_Thread4 <CTestThreadDlg> m_Thread1; C_Thread4 <CTestThreadDlg> m_Thread2;
public: // Функция первого потока (обязательно должна быть public) void Beep1(void) { while(true) { MessageBeep(MB_OK); Sleep(1000); } }
// Функция второго потока (обязательно должна быть public) void Beep2(void) { while(true) { MessageBeep(MB_ICONEXCLAMATION); Sleep(1000); } }
...
BOOL CTestThreadDlg::OnInitDialog() { CDialog::OnInitDialog();
// Запускаем потоки (первый параметр - указатель на текущий объект, второй - имя функции потока) m_Thread1.Start(this, Beep1); Sleep(500); m_Thread2.Start(this, Beep2); ...
// Для приостановки потока использовать команду m_Thread1.pCWinThread()->SuspendThread(); // Ну и т.д.
Тук - плямс. Тук - плямс. Тук - плямс. Тук - плямс. Тук - плямс. Тук - плямс. Тук - плямс. Тук - плямс. Еще много, много плямс.
|
|
|
Записан
|
|
|
|
|