Страниц: 1 [2]
  Печать  
Автор Тема: Класс со своим потоком выполнения  (Прочитано 12798 раз)
oder
Гость
« Ответ #15 : Октября 21, 2009, 03:55:44 pm »

В чем проблемы? Делаем ещё один оберточный класс, который в своем конструкторе динамически создает и запускает основной, а в деструкторе ожидает и удаляет его.
Записан
Absolut
Full Member
***
Offline Offline

Сообщений: 179


Просмотр профиля
« Ответ #16 : Октября 21, 2009, 04:03:42 pm »

В чем проблемы? Делаем ещё один оберточный класс, который в своем конструкторе динамически создает и запускает основной, а в деструкторе ожидает и удаляет его.
Обернуть можно было и исходный вариант (без потоков внутри). Я к тому, что манипуляции с прятками потока внутрь класса получились бессмысленны.
Записан
oder
Гость
« Ответ #17 : Октября 24, 2009, 11:28:15 pm »

Ладно, сегодня выходной, так что прокомментирую немножко. Ищите стрелки (<--)

З.Ы.: Я например давненько юзаю свою оббертку над тредом. К примеру в винде (копипаст с какого-то из проектов)

// хедер
Код:
class CThread
{
public: // typedefs

  class CLocker
  {
  public: // constructor/destructor
    CLocker(
        const std::string & _Name = ""
      ) : m_Name(_Name),
          m_LockHandler(NULL) <-- Где написано, что NULL - невалидное значение для хендла объекта ядра?
    {
      SECURITY_ATTRIBUTES mutex_security;
      mutex_security.nLength = sizeof(SECURITY_ATTRIBUTES);
      mutex_security.lpSecurityDescriptor = NULL;
      mutex_security.bInheritHandle = TRUE; // <-- Захламлять дочерние процессы хендлами? Такое только по параметру!

      LPSTR MutexName = NULL;
      if (!m_Name.empty())
        MutexName = (LPSTR)m_Name.c_str();

      <-- Мютекс - жутко медленная штука. Её можно (и нужно) использовать лишь для синхронизации меж процессами или когда досмерти необходимо ждать несколько объектов.
      m_LockHandler = ::CreateMutexA(&mutex_security, FALSE, MutexName);
    }

    virtual ~CLocker() <-- Зачем virtual??? Это ж простой тип. Нечего от него порождаться.
    {
      Release(); <-- Откуда известно, что мютекс захвачен? Зачем лишний вход в ядро?
      <-- Release обернул, а на Close уже сил не хватило.
      if (NULL != m_LockHandler || <-- Где написано, что NULL - невалидное значение для хендла объекта ядра?
          INVALID_HANDLE_VALUE == m_LockHandler)
      {
        ::CloseHandle(m_LockHandler);
      }
    }

  <-- Раз есть virtual, значит будем наследоваться. Тогда protected ничего ни от кого сильно не спрячет.
  protected: // Copy-constructors

    CLocker(
        const CLocker & _Obj
      ){}

    CLocker & operator =(
        const CLocker & _Obj
      ) {return *this;}
 
  public: // Interfacess

    void Release()
    {
      if (NULL != m_LockHandler)
      {
        ::ReleaseMutex(m_LockHandler);
      }
    }

    void Wait(
        DWORD _TimeWait
      )
    {
      DWORD ResultWaiting = ::WaitForSingleObject(m_LockHandler, _TimeWait);
      if (WAIT_FAILED == ResultWaiting)
      {
         // ... <-- Ну и?
      }
    }

    const std::string & GetName() const
    {
      return m_Name;
    }

  private: // Attributes

    std::string m_Name;
    HANDLE      m_LockHandler;
  };

  class CLock
  {
  public: // constructor/destructor

    CLock(
        CLocker & _InitLocker,
        DWORD     _TimeWaiting = INFINITE
      ) : m_Locker(_InitLocker)
    {
      m_Locker.Wait(_TimeWaiting);
    }

    ~CLock()
    {
      m_Locker.Release();
    }

  protected: // Copy-constructor

  CLock operator =(
      const CLock & _Obj
    ) { return *this;}

   
  private: // Attributes

    CLocker & m_Locker;
  };

public: // Static methods <-- Должен быть private
  static DWORD WINAPI RealThreadStart (
      LPDWORD _ObjThis
    );
 
public: // Constructor/Destructor

  CThread(
      bool _IsSuspend,
      int  _ExecuteTimeout
    );

  virtual ~CThread();

protected: // Copy-constructor <-- То же самое: при наследовании не защищает. А здедсь оно явно может быть.

  CThread(
      const CThread & _Obj
    )
  {}

  CThread & operator =(
      const CThread & _Obj
    ) {return *this;}

public: // Interfaces

  virtual void Execute(); <-- Не буду объяснять почему, но этот не должен быть виртуальным. Может сам поймешь когда-нибудь, когда грабли полезут.
  virtual bool ExecuteLoopIteration();

  void Suspend();

  void Resume();

  virtual void Terminate();

  bool IsTerminated() const;

  bool IsSuspended() const;

  HANDLE GetThreadHandle() const;

  int GetDestroyTimeout() const;

  void SetDestroyTimeout(
      int _Timeout
    );

private: // Internal methodth

  bool Start();

private: // Attributes

  HANDLE  m_ThreadHandle;
  HANDLE  m_EventTerminate;
  HANDLE  m_EventExit; <-- Вот этого не нужно вообще. На хендл потока можно так же успешно ждать, как и на событие.

  bool    m_IsSuspended;
  bool    m_IsTerminated;

  int     m_DestroyTimeout; <-- Почему int? Должен быть DWORD. Вернее, этого - первого - вообще не должно быть.
  int     m_ExecuteTimeout; <-- То же
};

#endif // __THREAD_H_INCLUDED__

// спп
Код:
#include "Thread.h"

#define DESTROY_HANDLE(pHandle)  if(pHandle != NULL){ ::CloseHandle(pHandle); pHandle=NULL;} <-- Кто сказал, что NULL - невалидное значение?

//
// Static methods
//

DWORD WINAPI CThread::RealThreadStart(
    LPDWORD _ObjThis
  )
{
  CThread * pThread = reinterpret_cast<CThread *> (_ObjThis);

  ::ResetEvent(pThread->m_EventTerminate); <-- А их что создавали поднятыми? Зачем лишний вход в ядро?
  ::ResetEvent(pThread->m_EventExit);

  pThread->Execute();

  ::SetEvent(pThread->m_EventExit);
  return 0;
}

// 
// Constructor/Destructor
//

CThread::CThread(
    bool _IsSuspend,
    int  _ExecuteTimeout
  ) : m_ThreadHandle(NULL),
      m_EventTerminate(NULL),
      m_EventExit(NULL),
      m_IsTerminated(false),
      m_IsSuspended(_IsSuspend),
      m_DestroyTimeout(INFINITE),
      m_ExecuteTimeout(_ExecuteTimeout)
{
  m_EventTerminate = ::CreateEvent(NULL, FALSE, FALSE, NULL); <-- А если не создался? <-- Это событие надо делать с ручным сбросом. Иначе, очень надо внимательно следить за кодом и гарантировать, что после срабатывания ожидания уже будет только выход по else из всей иерархии вызовов с false. Везде и всегда! А иначе -  событие-то уже сбросилось: второй раз на него попадёшь - зависнешь в ожидании навечно.
  m_EventExit      = ::CreateEvent(NULL, FALSE, FALSE, NULL); <-- Этого не нужно вовсе, но, если делать, - то, тоже, с ручным сбросом.

  if(!Start())
  {
    m_IsTerminated = true;
    ::SetEvent(m_EventExit);
  }
}

CThread::~CThread()
{
  DWORD ExitCode = 0;
  DWORD EventResult;
  Terminate();
  if (NULL != m_EventExit) <-- INVALID_HANDLE_VALUE, а не NULL!
  {
    EventResult = WaitForSingleObject(
                      m_ThreadHandle,
                      m_DestroyTimeout); <-- Ждать надо всегда до INFINITE!

    switch(EventResult)
    {

    case WAIT_TIMEOUT:
      {
        ::TerminateThread(m_ThreadHandle, ExitCode);<-- Никогда! Хоть бы умирал! Хоть бы весь мир рушился! Никогда так не делай! Программировать учиться надо, а не потоки убивать.
      } break;
    }//switch
  }

  DESTROY_HANDLE(m_ThreadHandle);
  DESTROY_HANDLE(m_EventTerminate);
  DESTROY_HANDLE(m_EventExit);
}

//
// Interfaces
//

void CThread::Execute()
{
  do
  {
    if(!ExecuteLoopIteration())
    {
      m_IsTerminated = true;
      break;
    }

    // check terminate event
    DWORD ResultWaiting = ::WaitForSingleObject(m_EventTerminate, m_ExecuteTimeout);
    switch (ResultWaiting)
    {

    case WAIT_OBJECT_0:
    case WAIT_FAILED:
      {
        m_IsTerminated = true; <-- Ты ж и так его выставляешь в true перед поднятием события. Если тебе WAIT_OBJECT_0 сработал - тут уже нечего присваивать. А если WAIT_FAILED - надо какой-то след об ошибке оставить. А то исчез поток посредь бела дня и на тебе!
      } break;
    }
  } while (!m_IsTerminated);
}

bool CThread::ExecuteLoopIteration()
{
  return true;
}

void CThread::Suspend() <-- Зачем это? Чтоб повыпендриваться? Там, где может быть нужен Suspend, никто такими классами не оперирует. Мне за мою программисткую жизнь (13+ лет) Suspend понадобился один-единственный раз. И то, без него можно было спокойно обойтись.
<-- Нет дважды понадобился. :) Я ещё раз утилиту себе для приостановки процессов делал (когда запускаешь что-нибудь, что долго винт долбит, а потом надо быстренько что-то сделать - как раз то, что нужно).
{
  m_IsSuspended = true; <-- И что? А если я два раза вызову?
  if(NULL != m_ThreadHandle)
  {
    ::SuspendThread(m_ThreadHandle);
  }
}

void CThread::Resume()
{
  m_IsSuspended = false; <-- А если я Suspend и не вызывал?
  if(NULL != m_ThreadHandle)
  {
    ::ResumeThread(m_ThreadHandle);
  }
}

void CThread::Terminate()
{
  m_IsTerminated = true;
  ::SetEvent(m_ThreadHandle); <-- Чего-чего? Какой ещё SetEvent(m_ThreadHandle)? Вот так у тебя все и работает!
}

bool CThread::IsTerminated() const
{
  return m_IsTerminated;
}

bool CThread::IsSuspended() const
{
  return m_IsSuspended;
}

HANDLE CThread::GetThreadHandle() const
{
  return m_ThreadHandle;
}

//
// Internal methodth
//

bool CThread::Start()
{
  HANDLE Handle = NULL;
  DWORD Suspended = 0; <-- Не Suspended, а Flags - переменные по смыслу называть надо.
  if(m_IsSuspended)
    Suspended = CREATE_SUSPENDED;

  // Create thread
  Handle = ::CreateThread( 
                NULL,                                     //атрибуты безопасности
                0,                                        //размер стека
                (LPTHREAD_START_ROUTINE )RealThreadStart, //метод запуска нити
                (LPVOID)this,                             //параметр метода запуска нити
                Suspended,                                //приостановление нити
                NULL                                     
              );


  if(NULL == Handle)
    return false;

  m_ThreadHandle = Handle;

  return true;
}
« Последнее редактирование: Октября 24, 2009, 11:32:41 pm от oder » Записан
Страниц: 1 [2]
  Печать  
 
Перейти в: