| 
			| 
					
						| 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.html Вадclass 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();
 // Ну и т.д.
 
 
Тук - плямс. Тук - плямс. Тук - плямс. Тук - плямс. Тук - плямс. Тук - плямс. Тук - плямс. Тук - плямс. Еще много, много плямс.   |  
						| 
								|  |  
								|  |  Записан | 
 |  |  | 
	|  |