Выполнить функцию в другом потоке
От: Amor Россия  
Дата: 24.06.04 14:29
Оценка:
Привет всем.

Просьба не пинать ногами, уверен, что уже обсуждали эту тему и не раз, до меня даже доносились обрывки этих разговоров в форумах, только вот я не понял по каким словам их искать в поиске. Попробовал — не получилось. Подскажите, плиз, эти ключевые слова, или ссылки, либо чем-нить еще.

Задача:
Есть ряд вызовов в общем виде.
ret = pobj->func( param1, ..., param2 ) ;
ret = obj.func( param1, ..., param2 ) ;
ret = func( param1, ..., param2 ) ;


Нужно создать такой механизм, чтобы выполнить этот вызов в отдельном потоке. А еще лучше в конкретном потоке, который уже работает (схожие наверно задачи, если первая будет, то и до второй недалеко).

1. Желательно, чтобы вызов был как наименее громоздким. Т.е. что-то типа такого:
RUN_IN_OTHER_THREAD( pobj, func, param1, ..., param2) ;

2. Учитывать виртуальные фукцнии объектов.
3. Определить возможность обратного вызова с полученным результатом и возможность ожидания исполнения (т.е. возврат после того как функция выполнит свою работу).


Заранее благодарен.
Re: Выполнить функцию в другом потоке
От: Кодт Россия  
Дата: 24.06.04 14:49
Оценка:
Здравствуйте, Amor, Вы писали:

A>Задача:

A>Есть ряд вызовов в общем виде.
A>
A>ret = pobj->func( param1, ..., param2 ) ;
A>ret = obj.func( param1, ..., param2 ) ;
A>ret = func( param1, ..., param2 ) ;
A>


A>Нужно создать такой механизм, чтобы выполнить этот вызов в отдельном потоке. А еще лучше в конкретном потоке, который уже работает (схожие наверно задачи, если первая будет, то и до второй недалеко).


A>1. Желательно, чтобы вызов был как наименее громоздким. Т.е. что-то типа такого:

A>
A>RUN_IN_OTHER_THREAD( pobj, func, param1, ..., param2) ;
A>

A>2. Учитывать виртуальные фукцнии объектов.
A>3. Определить возможность обратного вызова с полученным результатом и возможность ожидания исполнения (т.е. возврат после того как функция выполнит свою работу).

Использовать функторы, например, boost::function + boost::bind.

typedef boost::function0<void> func_type;

struct job_type
{
  func_type func;
  HANDLE done;

  job_type() : job_done(0) {}
  job_type(func_type const& f, HANDLE d = 0) : func(f), done(d) {}
};

queue<job_type> jobs;
HANDLE there_are_jobs;
CRITICAL_SECTION job_guard;

worker_thread()
{
  while(true)
  {
    WaitForSingleObject(there_are_jobs, INFINITE);
    EnterCriticalSection(&job_guard);
    job_type job = jobs.front();
    jobs.pop();
    LeaveCriticalSection(&job_guard);
    job.func();
    if(job.done) SetEvent(job.job_done);
  }
}

void post_job(const func_type& func, HANDLE done = 0)
{
  EnterCriticalSection(&job_guard);
  jobs.push_back(job_type(func,done));
  LeaveCriticalSection(&job_guard);
}

void send_job(const func_type& func)
{
  HANDLE done = CreateEvent(NULL,true,false,NULL);
  post_job(func);
  WaitForSingleObject(done);
  CloseHandle(done);
}


(Писал ногами на коленке. Где что не так — извиняйте).
Перекуём баги на фичи!
Re: Выполнить функцию в другом потоке
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 24.06.04 14:56
Оценка:
Здравствуйте, Amor, Вы писали:

http://rsdn.ru/article/baseserv/threadpool.xml
Автор(ы): Алексей Ширшов
Дата: 03.08.2003
Статья посвящена системным механизмам, организующим (или помогающим организовать) пул потоков. Рассматриваются базовые, универсальные сервисы, с помощью которых можно реализовывать серверы для любых доступных механизмов взаимодействия сервера и клиента: сокеты, именованные каналы (named pipes), почтовые ящики (mailslots) и проч.
... << RSDN@Home 1.1.0 stable >>
и солнце б утром не вставало, когда бы не было меня
Re[2]: Выполнить функцию в другом потоке
От: Amor Россия  
Дата: 28.06.04 07:02
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Использовать функторы, например, boost::function + boost::bind.


Сочинил вот такой код, только вот почему-то не могу скомпилить его.
class IRefCounter
{
public:
     virtual void AddRef() = 0 ;
     virtual void Release() = 0 ;
};

// Макросы - фукнции реализации IRefCounter
#define REF_COUNTER_IMPLEMENTATION  \
     private: \
          int m_iCntr ; \
     public: \
          virtual void AddRef() \
          { \
               ++m_iCntr ; \
          } \
          virtual void Release() \
          { \
               if ( --m_iCntr <= 0 ) \
               { \
                    delete this ; \
               } \
          }
#define REF_COUNTER_INIT m_iCntr( 0 )

// Поток будет вызывать эту функцию
class ICall : public IRefCounter
{
public:
     virtual void Call() = 0 ;
//     virtual void Exception( std::exception& st ) = 0 ; ... наметки
};

// Это фукнция потока
enum { WM_CALL = WM_USER } ;
DWORD WINAPI ThrFunc( LPVOID pParam )
{
     MSG msg ;
     while ( GetMessage( &msg, 0, 0, 0 ) )
     {
          if ( msg.message == WM_CALL ) // Надо совершить вызов
          {
               ICall * pCall = ( ICall * ) msg.lParam ;
               try
               {
                    pCall->Call() ; // совершаем
                    if ( msg.wParam )
                    {
                         SetEvent( (HANDLE) msg.wParam ) ;
                    }
                    pCall->Release() ;
               }
               catch ( std::exception& )
               {
                    //pCall->Exception( st ) ;
               }
          }
     }

     return 0 ;
}

// Вызов неконстантной фукнции, возвращающей значение (не void), с двумя параметрами.
template< class _R, class BinOp, class _A1, class _A2 >
class CFunction2 : public ICall
{
     REF_COUNTER_IMPLEMENTATION
public:
     CFunction2( const BinOp& obj, _R ( BinOp::*pfn)(_A1,_A2), _A1 arg1, _A2 arg2 ) 
          : m_obj( obj )
          , m_pfn( pfn )
          , m_arg1( arg1 )
          , m_arg2( arg2 )
          , REF_COUNTER_INIT
     {}

     virtual void Call()
     {
          m_result = (m_obj.*m_pfn)( m_arg1, m_arg2 ) ;
     }

     typedef _R result_type ;
     _R m_result ;

protected:
     BinOp m_obj ;
     _R ( BinOp::*m_pfn )(_A1,_A2) ;
     _A1 m_arg1 ;
     _A2 m_arg2 ;
} ;

template< class _R, class BinOp, class _A1, class _A2 >
CFunction2< _R, BinOp, _A1, _A2 > * Function2( const BinOp& obj, _R ( BinOp::*pfn)(_A1,_A2), _A1 arg1, _A2 arg2 )
{
     CFunction2< _R, BinOp, _A1, _A2 > * p = new CFunction2< _R, BinOp, _A1, _A2 >( obj, pfn, arg1, arg2 ) ;
     p->AddRef() ;
     return p ;
}

// Также надо сделать.
// const_Function2, void_Function2, const_void_Function2... или механизм для void использовать (я уже встречал варианты реализации)
// И тоже самое для Function0, Function1, Function3 и, далее по мере необходимости.

class CTmp
{
public:
     int func2( int a, int b )
     {
          return a+b ;
     }
};

// Поток обслуживающий эти вызовы.
class CCaller
{
     DWORD dwThrId ;
     HANDLE hThread ;
public:
     // Конструктор - создаем поток.
     CCaller()
     {
          hThread = CreateThread( NULL, 0, ThrFunc, NULL, 0, &dwThrId ) ;
          Sleep( 1000 ) ;
     }
     // Деструктор - завершаем его.
     ~CCaller()
     {
          PostThreadMessage( dwThrId, WM_QUIT, 0, 0 ) ;
          WaitForSingleObject( hThread, INFINITE ) ;
          CloseHandle( hThread ) ;
     }

     // Совершить вывов функции нашем потоке.
     template< class BinObj >
     void Call( BinObj * pCall )
     {
          PostThreadMessage( dwThrId, WM_CALL, 0, ( LPARAM ) ( ICall * ) pCall ) ;
     }

     // Совершить вызов функции в потоке с ожиданием результата.
     template< class BinObj >
     typename BinObj::result_type Call_Wait( BinObj * pCall )
     {
          HANDLE hEvent = CreateEvent( NULL, FALSE, FALSE, NULL ) ;
          pCall->AddRef() ;
          PostThreadMessage( dwThrId, WM_CALL, ( WPARAM )hEvent, ( LPARAM ) ( ICall * ) & pCall ) ;
          WaitForSingleObject( hEvent, INFINITE ) ;
          CloseHandle( hEvent ) ;
          typename BinObj::result_type res = rCall.m_result ;
          rCall.Release() ;
          return  res ;
     }
};

int main(int argc, char* argv[])
{
     CTmp tmp ;
     CCaller caller ;

     int res = caller.Call_Wait( Function2( tmp, &CTmp::func2, 2, 2 ) ) ;
/*
В строке выше - ошибка.
error C2893: Failed to specialize function template 'generic-type-271 __thiscall CCaller::Call_Wait(BinObj *)'
        With the following template arguments:
        'class CFunction2<int,class CTmp,int,int>'
*/

     caller.Call( Function2( tmp, &CTmp::func2, 2, 2 ) ) ;

     return 0;
}


В чем ошибка, подскажите, а то у меня уже....
Как вам вообще такая реализация.

Заранее благодарен.
Re[3]: Небольшая ошибочка
От: Amor Россия  
Дата: 28.06.04 07:26
Оценка:
Было вот так:

A>
A>     // Совершить вызов функции в потоке с ожиданием результата.
A>     template< class BinObj >
A>     typename BinObj::result_type Call_Wait( BinObj * pCall )
A>     {
A>          HANDLE hEvent = CreateEvent( NULL, FALSE, FALSE, NULL ) ;
A>          pCall->AddRef() ;
A>          PostThreadMessage( dwThrId, WM_CALL, ( WPARAM )hEvent, ( LPARAM ) ( ICall * ) & pCall ) ;
A>          WaitForSingleObject( hEvent, INFINITE ) ;
A>          CloseHandle( hEvent ) ;
A>          typename BinObj::result_type res = rCall.m_result ;
A>          rCall.Release() ;
A>          return  res ;
A>     }
A>


Надо так:
     template< class BinObj >
     typename BinObj::result_type Call_Wait( BinObj * pCall )
     {
          HANDLE hEvent = CreateEvent( NULL, FALSE, FALSE, NULL ) ;
          pCall->AddRef() ;
          PostThreadMessage( dwThrId, WM_CALL, ( WPARAM )hEvent, ( LPARAM ) ( ICall * ) & pCall ) ;
          WaitForSingleObject( hEvent, INFINITE ) ;
          CloseHandle( hEvent ) ;
          typename BinObj::result_type res = pCall->m_result ;
          pCall->Release() ;
          return  res ;
     }
Re[2]: Выполнить функцию в другом потоке
От: PoM-PoM 40mm Россия  
Дата: 28.06.04 08:02
Оценка:
А напишите ПЛЗ пример использования. Допустим с парой параметров.

Попутно вопрос -- допустим есть некоторый класс, имеющий оператор ().

class SomeCleverClass
{
   public:
    operator() () 
     {
      //какая-то бурная деятельность
     }
 };


допустим мы хотим вызвать его через boost::function0<void>

boost::function0<void> func;
 
 {
   SomeCleverClass stupid;

    func=stupid;    
 }

func(); //  а не рухнет ли тут из-за вызова по разрушенному объекту!


Если рухнет, то оно у Вас не очень удобно -- все вызовы только через Send, впрочем проблема вообще философская
Will give me piece of mind
Re[3]: Выполнить функцию в другом потоке
От: Amor Россия  
Дата: 28.06.04 08:14
Оценка:
Здравствуйте, PoM-PoM 40mm, Вы писали:

PP4>А напишите ПЛЗ пример использования. Допустим с парой параметров.


PP4>Попутно вопрос -- допустим есть некоторый класс, имеющий оператор ().


PP4>
PP4>class SomeCleverClass
PP4>{
PP4>   public:
PP4>    operator() () 
PP4>     {
PP4>      //какая-то бурная деятельность
PP4>     }
PP4> };
PP4>


PP4>допустим мы хотим вызвать его через boost::function0<void>


PP4>
PP4>boost::function0<void> func;
 
PP4> {
PP4>   SomeCleverClass stupid;

PP4>    func=stupid;    
PP4> }

PP4>func(); //  а не рухнет ли тут из-за вызова по разрушенному объекту!
PP4>


PP4>Если рухнет, то оно у Вас не очень удобно -- все вызовы только через Send, впрочем проблема вообще философская


Это ко мне вопрос?
Често, я в бустах вообще не разбираюсь. Никогда им не пользовался. Может и зря
Re[4]: Выполнить функцию в другом потоке
От: PoM-PoM 40mm Россия  
Дата: 28.06.04 08:44
Оценка:
Э нет скорее к Кодт'у
Will give me piece of mind
Re[3]: Выполнить функцию в другом потоке
От: Кодт Россия  
Дата: 28.06.04 08:50
Оценка:
Здравствуйте, PoM-PoM 40mm, Вы писали:

PP4>
PP4>boost::function0<void> func;
 
PP4> {
PP4>   SomeCleverClass stupid;

PP4>    func=stupid;    // (*)
PP4> }

PP4>func(); //  а не рухнет ли тут из-за вызова по разрушенному объекту!
PP4>

Не рухнет. (*) Будет создана копия объекта SomeCleverClass.
Перекуём баги на фичи!
Re[4]: Выполнить функцию в другом потоке
От: PoM-PoM 40mm Россия  
Дата: 28.06.04 09:25
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Здравствуйте, PoM-PoM 40mm, Вы писали:


PP4>>
PP4>>boost::function0<void> func;
 
PP4>> {
PP4>>   SomeCleverClass stupid;

PP4>>    func=stupid;    // (*)
PP4>> }

PP4>>func(); //  а не рухнет ли тут из-за вызова по разрушенному объекту!
PP4>>

К>Не рухнет. (*) Будет создана копия объекта SomeCleverClass.

Замечательно. А Вы не просветите меня как это чудо реализовано? Немного покопался в исходниках, понял ничтожно мало.
Will give me piece of mind
Re[5]: Выполнить функцию в другом потоке
От: Кодт Россия  
Дата: 28.06.04 11:12
Оценка: 17 (3)
Здравствуйте, PoM-PoM 40mm, Вы писали:

К>>Не рухнет. (*) Будет создана копия объекта SomeCleverClass.


PP4>Замечательно. А Вы не просветите меня как это чудо реализовано? Немного покопался в исходниках, понял ничтожно мало.


О, это страшное дело.
Вкратце, так (исключительно вольное переложение устройства boost::function) :

Сперва — избавимся от излишнего ассортимента типов, приводя все функторы к одному виду
typedef void* vptr;
typedef void(*vfun)();

union any_ptr
{
  vptr p;
  vfun f;
};
any_ptr make_any() { return any_ptr(); }
any_ptr make_any(vptr p)  { any_ptr ap; ap.p = p; return ap; }
any_ptr make_any(vfun f)  { any_ptr ap; ap.f = f; return ap; }

Естественно, что при этом мы потеряли три важных метода — вызов operator(), конструктор копирования и деструктор (для функциональных объектов).

Поэтому вынесем это умение...
void destroy_fun(any_ptr ap) {} // глобоальные функции невозможно разрушать
template<class T> void destroy_ptr(any_ptr ap) { delete (T*)(ap.p); } // объекты - разрушаем с помощью delete

any_ptr copy_fun(any_ptr ap) { return ap; } // копирование сводится к копированию указателя
template<class T> any_ptr copy_ptr(any_ptr ap) { return make_any(new T(*(T*)(ap.p))); } // объект - конструируем

template<class R, class A1, class A2, .....>
R invoke_fun(any_ptr ap, A1 a1, A2 a2, ...) { return ((R(*)(A1,A2,...))(ap.f))(a1,a2,...); }
template<class T, class R, class A1, class A2, .....>
R invoke_ptr(any_ptr ap, A1 a1, A2 a2, ...) { return ((T*)(ap.p))->operator()(a1,a2,...); }
// обратите внимание: по типу T невозможно вывести типы аргументов и результата,
// поскольку у объекта могут быть несколько сигнатур operator().

... и будем хранить рядом с any_ptr
struct function_base_
{
  any_ptr ap;
  vfun copier; // здесь мы не теряем информацию о типе: её знает класс function<...>
  vfun destroyer;
  vfun invoker;
};

Поскольку эти три метода выступают вместе, можно сделать компактнее
enum action { to_copy, to_destroy, to_invoke };

template<class R, class A1, class A2, ...>
vfun manage_fun(any_ptr ap, action act)
{
  switch(act)
  {
  case to_copy: return ap; // возвращает копию
  case to_destroy: return ap; // возвращает бессодержательное значение
  case to_invoke: return invoke_fun<R,A1,A2,...>; // возвращает функцию-пускатель
  }
}

template<class T, class R, class A1, class A2, ...>
vfun manage_ptr(any_ptr ap, action act)
{
  switch(act)
  {
  case to_copy: return make_any(new T(*(T*)ap.p));
  case to_destroy: delete (T*)ap.p; return ap;
  case to_invoke: return invoke_ptr<T,R,A1,A2,...>;
  }
}

(Поскольку вызов функционального объекта нельзя свести к вызову функции — промежуточная N+1-арная функция-пускатель нам нужна).

struct function_base
{
  any_ptr ap;
  vfun (*manager)(action);

  function_base(any_ptr a, vfun m)
  : ap(a)
  , manager(m)
  {}

  function_base(const function_base& src)
  : ap(src.manager(src.ap,to_copy))
  , manager(src.manager)
  {}

  ~function_base()
  {
    src.manager(src.ap,to_destroy);
  }

  function_base& operator=(const function_base& src)
  {
    if(this != &src)
    {
      manager(ap,to_destroy);
      ap = src.manager(src.ap,to_copy);
      manager = src.manager;
    }
    return *this;
  }
};


Теперь осталось научиться создавать, вызывать и убивать типизированные функции:
struct bad_function_call : runtime_error
{
  bad_function_call() : runtime_error("call to uninitialized boost::function");
};

void throw_bad_function_call()
{
  throw bad_function_call;
}

template<class R, class A1, class A2, ...>
class function : function_base
{
public:
  // создание
  function() : function_base( make_ap(throw_bad_function_call), manage_fun<R,A1,A2,...> )
  {}
  function(R(*f)(A1,A2,...)) : function_base( make_ap(f), manage_fun<R,A1,A2,...> )
  {}
  template<class T>
  function(const T& obj) : function_base( make_ap(obj), manage_fun<T,R,A1,A2,...> )
  {}

  // вызов
  R operator()(A1 a1, A2 a2, ...) const
  {
    typedef R(*invoker)(any_ptr,A1,A2,...);
    return ((invoker)(manager(ap,to_invoke)))(ap,a1,a2,...);
  }
};


За кадром осталось дофига нюансов, недомолвок, передёргиваний и уточнений — связанных с константностью, видимостью, поддержкой разных компиляторов...
Перекуём баги на фичи!
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.