Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: [1]   Вниз
  Печать  
Автор Тема: Поток на основе функции класса.  (Прочитано 18891 раз)
0 Пользователей и 1 Гость смотрят эту тему.
CTAPOBEP
Постоялец

ru
Offline Offline
Пол: Мужской

« : 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-е и все последующие могут добавить сюда все желающие.

И еще просьба - не надо, шерсть на носу, задавать вопрос "зачем?", я все равно не знаю на него ответа :ф)


Записан
Вад
Модератор

ru
Offline Offline
Пол: Мужской

« Ответ #1 : 17-10-2008 09:22 » 

Я не буду задавать вопрос "Зачем". Мне интересно, почему - макросы Улыбаюсь Объявление сущности потока - нечитабельное, пункт 1 приводит к появлению загадочного кода. Человек, сопровождающий твой код, тебя проклянёт Улыбаюсь

Может, стоило всё-таки шаблоном? Улыбаюсь
« Последнее редактирование: 17-10-2008 10:19 от Вад » Записан
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #2 : 17-10-2008 09:24 » 

как бы тебе ни хотелось Улыбаюсь Зачем? У меня и без дополнительного класса всё неплохо получается
Записан

zubr
Гость
« Ответ #3 : 17-10-2008 10:51 » 

Я бы к данному классу добавил методы:
Terminate - завершение потока
WaitFor - ожидание завершения потока
переменную Terminated - обозначающую состояние потока. К примеру в цикле потока можно было бы писать:
while (! Terminated)
{
.......
}
Записан
CTAPOBEP
Постоялец

ru
Offline Offline
Пол: Мужской

« Ответ #4 : 18-10-2008 06:42 » 

Цитата
Объявление сущности потока - нечитабельное, пункт 1 приводит к появлению загадочного кода. Человек, сопровождающий твой код, тебя проклянёт

Лишний повод поупражняться в написании комментариев. Любите ли вы писать комментарии, как люблю писать их я?
Впрочем, насчет шаблона - что-то тут есть...

Цитата
У меня и без дополнительного класса всё неплохо получается

А у меня - нет. Ну не поднимается у меня рука объявить глобальную функцию.
Не любите ли вы объявлять глобальные функции, как любит их объявлять не я?

Цитата
Я бы к данному классу добавил методы:
Terminate - завершение потока
WaitFor - ожидание завершения потока
переменную Terminated - обозначающую состояние потока. К примеру в цикле потока можно было бы писать:
while (! Terminated)
{
.......
}

Функция потока в данном случае - обычная функция класса, она имеет доступ ко всем членам класса, в котором объявлена, поэтому все это организуется элементарно. Собственно, макрос был написан просто для удобства объявления потока.

« Последнее редактирование: 18-10-2008 06:56 от CTAPOBEP » Записан
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #5 : 18-10-2008 07:01 » 

CTAPOBEP,

Цитата
А у меня - нет. Ну не поднимается у меня рука объявить глобальную функцию.
Не любите ли вы объявлять глобальные функции, как любит их объявлять не я?
никогда не делал глобальной Улыбаюсь Беру в любом удобном мне классе, пишу статическую static DWORD F(void*); -и готово.

Цитата
Функция потока в данном случае - обычная функция класса, она имеет доступ ко всем членам класса, в котором объявлена, поэтому все это организуется элементарно. Собственно, макрос был написан просто для удобства объявления потока.

 А доступ ко всем членам класса - оно зачем такая радость ? Передай, что нужно, через указатели и всё.

А отлаживать макрос затрахолебаешься Улыбаюсь
Писал я раньше большие макросы, теперь предпочитаю их функцией оформлять просто потому, что отлаживать удобнее
« Последнее редактирование: 18-10-2008 09:28 от Алексей1153++ » Записан

Вад
Модератор

ru
Offline Offline
Пол: Мужской

« Ответ #6 : 18-10-2008 07:59 » 

Цитата
Объявление сущности потока - нечитабельное, пункт 1 приводит к появлению загадочного кода. Человек, сопровождающий твой код, тебя проклянёт

Лишний повод поупражняться в написании комментариев. Любите ли вы писать комментарии, как люблю писать их я?
Intel точно не любит писать комментарии Улыбаюсь Дело в том, что я над подобными комбинациями по конструированию имён функций и глобальных переменных с помощью макросов во время компиляции, в том числе и по месту их вызова, медитировал довольно много времени, пытаясь понять, как интеловская OpenCV использует плагины вместо стандартных реализаций функций Улыбаюсь  Но там авторы, видимо, не определились, на чём они пишут - C или C++ Улыбаюсь

Вообще, ход мысли мне немного напоминает реализацию потоков на Java - там, сколь мне помнится, есть интерфейс Runnable, который содержит публичный метод run. Когда создаёшь поток, можешь передать ему Runnable в конструктор, и в качестве своей функции он запустит именно Runnable.run(). По идее, C++ позволяет делать подобные вещи, и это смотрелось бы более логично, чем вместо интерфейса генерировать методы прямо в классе, где требуется поток.

Разница только в том, что может потребоваться более одного потока. То есть, в общем-то, нужна только возможность параметризации вызываемой функции - для таких более сложных случаев завязка на интерфейс уже не помогает, и надо как-то реализовать конкретно параметризацию. Имхо, проще и нагляднее это сделать предъявлением к функции определённых требований (соответствия типу) и передачи указателя на неё явно в качестве параметра при старте потока. Но можно подумать и над более изящным решением Улыбаюсь

Впрочем, всегда можно сказать, что более одного потока, непосредственно принадлежащего одной сущности - это дефект проектирования Улыбаюсь
Записан
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #7 : 18-10-2008 08:20 » 

Цитата
Впрочем, всегда можно сказать, что более одного потока, непосредственно принадлежащего одной сущности - это дефект проектирования
это почему вдруг ? )
Записан

CTAPOBEP
Постоялец

ru
Offline Offline
Пол: Мужской

« Ответ #8 : 18-10-2008 09:22 » 

Цитата
Беру в любом удобном мне классе, пишу статическую static DWORD F(void*); -и готово.
Передай, что нужно, через указатели и всё.

Макрос как раз для автоматизации всего этого.

Цитата
теперь предпочитаю их йункцией оформлять просто потому, что отлаживать удобнее

В случае использования нескольких потоков для каждого потока нужна своя функция, поэтому макрос и понадобился. Кстати, по той же причине и шаблон тут не получается.

Записан
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #9 : 18-10-2008 09:31 » 

Цитата
В случае использования нескольких потоков для каждого потока нужна своя функция
ой, ты чО Улыбаюсь У меня одна функция, а потоков из неё можно сделать хоть 100. А если код потоков должен быть разный - то макрос сложно станет отлаживать. Насчёт шаблона не знаю - не пользуюсь (не признаЮ вернее Улыбаюсь )
Записан

Вад
Модератор

ru
Offline Offline
Пол: Мужской

« Ответ #10 : 18-10-2008 14:00 » 

Цитата
Впрочем, всегда можно сказать, что более одного потока, непосредственно принадлежащего одной сущности - это дефект проектирования
это почему вдруг ? )
Потому что у тебя выходит сущность, которая сама, непосредственно, выполняет разнородные действия в двух параллельных потоках. Скорее всего, такая сущность несёт две разных функции (или зачем-то непосредственно поддерживает исполнение двух одинаковых функций параллельно). А это уже похоже на проблемы декомпозиции Ага
Записан
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #11 : 18-10-2008 14:54 » 

Вад, не могу понять, на пальцах покажи Улыбаюсь
Записан

Вад
Модератор

ru
Offline Offline
Пол: Мужской

« Ответ #12 : 18-10-2008 21:25 » 

Если на пальцах, то зачем нужны потоки? Чтобы обрабатывать поступающую информацию и реагировать асинхронно, в фоне и не мешая всем прочим.

Вариант первый: когда у тебя два потока - одинаковые. То есть, они полностью идентичны по выполняемым функциям, и только данные, которые они обрабатывают, различаются по значению (но одинаковые по типу). Тогда налицо два канала обработки данных. Возникает вопрос: а для чего они два (и более) являются непосредственными и неотделимыми частями общего класса? Как порождаются эти каналы? Как взаимодействуют между собой? Имхо, довольно стандартное решение - инкапсулировать такой канал в виде отдельного класса, и при необходимости иметь некий менеджер каналов, который ни сном ни духом об их логике работы, а только управляет количеством.

Вариант второй - когда обрабатываются различные данные и по различным принципам. Тут, имхо, всё ещё проще: наличие у сущности двух различных функций - симптом неверной декомпозиции, нарушение принципа "одна сущность - одна функция" в том смысле, что сложную разнородную логику лучше разделять на более простые составные части хотя бы из соображений внятности и стройности архитектуры. Поток - это признак тяжеловесной обработки или специфической асинхронной реакции. Две различных реакции - это уже как-то странно. Наверное, всегда можно упростить логику её дальнейшей декомпозицией.
Записан
Антон (LogRus)
Глобальный модератор

ru
Offline Offline
Пол: Мужской
Внимание! Люблю сахар в кубиках!


WWW
« Ответ #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
Постоялец

ru
Offline Offline
Пол: Мужской

« Ответ #14 : 14-11-2008 05:28 » new

 
Шаблон до кучи.

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();
// Ну и т.д.


Тук - плямс. Тук - плямс. Тук - плямс. Тук - плямс. Тук - плямс. Тук - плямс. Тук - плямс. Тук - плямс. Еще много, много плямс.
 
 
Записан
Страниц: [1]   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines