Kód Win thread

Z CHWiki

Přejít na: navigace, hledání

Obsah

[editovat] Vlákna EXT knihovny

Šablony win::thread a win::threadc zapouzdřují tvorbu a správu vláken do jednoduchých objektů se zachováním objektové sémantiky. Tedy vlákno je považováno za objekt, vytváří se při konstrukci nosného objektu a zaniká dříve nebo společně s ním. Zaniká-li objekt za běhu vlákna, je destruktor pozdržen do doby skončení vlákna. Pro práci s vláknem je možno použít několik metod popsaných dále.

[editovat] win::thread

Tuto šablonu použijte k vytvoření vlákna, které provede kód globální funkce nebo statické metody. Funkce musí mít následující signaturu:

int procedure (void *) {
   // ...
   return 0;
};

Syntaxe k vytvoření vlákna je dvojí, s parametrem nebo bez:

win::thread <procedure> thread;
win::thread <procedure> thread (ptr);

Parametr ptr je ukazatel typu void * a specifikuje parametr, který se předává vláknu. Není-li uveden, předává se NULL.

[editovat] win::threadc

Tuto šablonu použijte k vytvoření vlákna, které provede kód metody v kontextu objektu. Metoda musí mít následující signaturu:

struct Class {
   int Method (void *) {
       // ...
       return 1;
   };
};

K vytvoření vlákna je možné použít jeden z následujícíh zápisů

win::thread <Class, &Class::Method> t2;
win::thread <Class, &Class::Method> t2 (ptr);

win::thread <Class, &Class::Method> t2 (object);
win::thread <Class, &Class::Method> t2 (object, ptr);

Podobně jako u win::threadc, je parametr ptr ukazatel typu void * a specifikuje parametr, který se předává vláknu. Není-li uveden, předává se NULL.

Je-li předán objekt (referencí), metoda v novém vlákně se volá v kontextu tohoto objektu. Není-li žádný objekt předán, vytvoří se jeden dočasný v kontextu nového vlákna a zaniká po skončí metody společně s vláknem.

[editovat] Metody

Obě výše zmíněné šablony vystavují tyto metody:

  • bool running () const throw ();
    • vrací true, pokud vlákno stále běží; false pokud již skončilo
  • bool failed () const throw ();
    • vrací true, pokud vlákno skončilo nezachycenou výjimkou, jinak false
  • int result () const throw ();
    • vrací návratovou hodnotu vlákna, pokud vlákno stále běží vrátí 259 (Windowsovská konstanta STILL_ACTIVE)
  • void wait () const throw ();
    • pozdrží provádění do momentu skončení vlákna
    • pokud vlákno už neběží, vrátí okamžitě

[editovat] Do dalších verzí

  • win::threadc rozšířit o možnost předávání konstantních referencí na objekt (omezení na vytváření vláken jen z konstantních metod)
  • doplnit více metod (minimálně id vlákna)

[editovat] Zdrojové kódy

[thread.hpp]

#ifndef WIN_THREAD_HPP
#define WIN_THREAD_HPP

namespace win {
   
   // thread_base, bazova trida pro thread a threadc

   class thread_base {
       private:
           class implementation;
           volatile implementation * pimpl;
       protected:
           thread_base (int (*) (void *), void *);
           ~thread_base () throw ();
       
       public:
           bool running () const throw ();
           bool failed () const throw ();
           
           int  result () const throw ();
           void wait () const throw ();
   };
   
   // thread <function> t1;
   // thread <function> t1 (void *);
   //  - int function (void *);
   
   template <int (*P) (void *)>
   class thread : private thread_base {
       public:
           thread (void * = 0);
           
       public:
           using thread_base::running;
           using thread_base::failed;
           using thread_base::result;
           using thread_base::wait;
   };
   
   // thread <Class, &Class::Method> t2;
   // thread <Class, &Class::Method> t2 (void *);
   // thread <Class, &Class::Method> t2 (object);
   // thread <Class, &Class::Method> t2 (object, void *);
   
   template <typename C, int (C::*P) (void *)>
   class threadc : private thread_base {
       private:
           C * const   object;
           const bool  owned;
           void *      arg;
           
           static int  caller (void *);
       
       public:
           threadc (void * = 0);
           threadc (C & c, void * = 0);
           ~threadc () throw ();
           
       public:
           using thread_base::running;
           using thread_base::failed;
           using thread_base::result;
           using thread_base::wait;
   };
};

#include "thread.tcc"
#endif

[thread.tcc]

#ifndef WIN_THREAD_TCC
#define WIN_THREAD_TCC

template <int (* ptr) (void *)>
win::thread <ptr> ::thread (void * arg)
   :   win::thread_base (ptr, arg) {};

template <typename C, int (C::*ptr) (void *)>
win::threadc <C, ptr> ::threadc (void * _arg)
   :   win::thread_base (caller, this),
       object (new C), owned (true), arg (_arg) {};

template <typename C, int (C::*ptr) (void *)>
win::threadc <C, ptr> ::threadc (C & c, void * _arg)
   :   win::thread_base (caller, this),
       object (&c), owned (false), arg (_arg) {};

template <typename C, int (C::*ptr) (void *)>
win::threadc <C, ptr> ::~threadc () throw () {
   this->wait ();
   if (this->owned)
       delete this->object;
};

template <typename C, int (C::*ptr) (void *)>
int win::threadc <C, ptr> ::caller (void * param) {
   win::threadc <C, ptr> * self =
       reinterpret_cast <win::threadc <C, ptr> *> (param);
   
   return (self->object->*ptr) (self->arg);
};

#endif

[thread.cpp]

#include "thread.hpp"
#include <windows.h>

class win::thread_base::implementation {
   private:
       int (* ptr) (void *);
       void * arg;
       
   public:
       bool    failed;
       HANDLE  hThread;
       DWORD   dwThreadId;
   
   private:
       static DWORD WINAPI procedure (LPVOID);
   
   public:
       implementation (int (*) (void *), void *);
};

win::thread_base::implementation::implementation (int (* _ptr) (void *),
                                                 void * _arg)
   :   ptr (_ptr),
       arg (_arg),
       failed (false),
       hThread (CreateThread (NULL, 0, procedure,
                              this, CREATE_SUSPENDED, &this->dwThreadId)) {
   
   ResumeThread (this->hThread);
   return;
};

DWORD WINAPI win::thread_base::implementation::procedure (LPVOID param) {
   volatile win::thread_base::implementation * self = 
       reinterpret_cast <volatile win::thread_base::implementation *> (param);
   
   try {
       return self->ptr (self->arg);
   } catch (...) {
       self->failed = true;
       return (DWORD) -1;
   };
};

win::thread_base::thread_base (int (* ptr) (void *), void * arg)
   :   pimpl (new implementation (ptr, arg)) {};

win::thread_base::~thread_base () throw () {
   this->wait ();
   delete this->pimpl;
          this->pimpl = NULL;
   return;
};

int win::thread_base::result () const throw () {
   DWORD dwResult (0);
   GetExitCodeThread (this->pimpl->hThread, &dwResult);
   
   return dwResult;
};
bool win::thread_base::running () const throw () {
   return WaitForSingleObject (this->pimpl->hThread, 0) != WAIT_OBJECT_0;
};
bool win::thread_base::failed () const throw () {
   return this->pimpl->failed;
};
void win::thread_base::wait () const throw () {
   WaitForSingleObject (this->pimpl->hThread, INFINITE);
   return;
};