обьект живущий в отдельном потоке
От: gid_vvp  
Дата: 12.02.05 13:14
Оценка:
Hi, All.

Может немного не по теме форума...
Но я не знаю куда запостить этот вопрос.

Необходимо сделать так чтобы некий объект (класс) полностью жил в отдельном потоке, т.е. чтобы все вызовы функций данного класса происходили в контексте его потока, даже если они вызываются из других потоков.

Возможно немного запутанно...
Но надеюсь на помощь.
Re: обьект живущий в отдельном потоке
От: tarkil Россия http://5209.copi.ru/
Дата: 12.02.05 13:41
Оценка: 7 (3)
Здравствуйте, gid_vvp, Вы писали:

_>Необходимо сделать так чтобы некий объект (класс) полностью жил в отдельном потоке, т.е. чтобы все вызовы функций данного класса происходили в контексте его потока, даже если они вызываются из других потоков.


По всей видимости, Вам нужно то, что именуется "живыми объектами", есть такая концепция. C++ напрямую её не поддерживает, так что придётся делать руками.

Идея такая: делаем реальный класс объекта FooImpl и некий класс-оболочку, которая будет доступна из других потоков — Foo. FooImpl никто не видит, все пользуются Foo, хранящий указатель на FooImpl (короче, это обычная концепция объект+интерфейс).

Ещё нам понадобится очередь, хранящая все обращения к FooImpl. Соответственно, поток, принадлежащий FooImpl (создаётся и грохается вместе с ним) делает одну простую вещь (здесь и далее используется псевдокод):

void FooThreadFunc( PVOID Param )
{
  FooImpl *Obj = static_cast<FooImpl*>( Param );

  for(;;)
  {
    Call &c = Obj->Queue.WaitForCall();
    c.Execute( Obj );
  }
}

Все методы Foo имеют примерно такой вид:

RetT Foo::FunnyMethod( T1 a1, T2 a2 )
{
  CallImpl<FunnyMethod, RetT, T1, T2> c( a1, a2 );
  m_Impl->Queue.MakeCall( c );
  return c.RetValue;
}

Очередь должна позаботиться, чтобы в MakeCall вызывающий поток подвис в ожидании, зато дождалась бы функция WaitForCall и исполнила запрос, после чего следующий WaitForCall снова б стал ждать, а MakeCall вернула управление. Это не то, чтобы совсем просто, но и не сложно сделать на объектах синхронизации Винды.

Самый затык это придумать класс Call (и сопутствующий типобезопасный класс CallImpl), инкапсулирующий в себя аргументы, передаваемые методу, возвращаемое значение и, собственно, какой-то идентификатор метода. Аналог IDispatch, короче, в его сторону я б и смотрел. Или у Александреску был пример разработки чего-то похожего в Loki.

Как сделать без такой сложной инкапсуляции вызова я не знаю, скорее всего никак.
--
wbr, Peter Taran
Re[2]: обьект живущий в отдельном потоке
От: gid_vvp  
Дата: 12.02.05 13:57
Оценка:
Здравствуйте, tarkil, Вы писали:


T>По всей видимости, Вам нужно то, что именуется "живыми объектами", есть такая концепция. C++ напрямую её не поддерживает, так что придётся делать руками.


T>Идея такая: делаем реальный класс объекта FooImpl и некий класс-оболочку, которая будет доступна из других потоков — Foo. FooImpl никто не видит, все пользуются Foo, хранящий указатель на FooImpl (короче, это обычная концепция объект+интерфейс).


T>Ещё нам понадобится очередь, хранящая все обращения к FooImpl. Соответственно, поток, принадлежащий FooImpl (создаётся и грохается вместе с ним) делает одну простую вещь (здесь и далее используется псевдокод):


Спасибо, я нечто подобное себе и представлял.

Может быть у кого-нибудь есть ещё какие-нибудь варианты?
Re: обьект живущий в отдельном потоке
От: ABK Украина  
Дата: 12.02.05 13:59
Оценка: 1 (1)
Здравствуйте, gid_vvp, Вы писали:

_>Необходимо сделать так чтобы некий объект (класс) полностью жил в отдельном потоке, т.е. чтобы все вызовы функций данного класса происходили в контексте его потока, даже если они вызываются из других потоков.


Можно в конструкторе запускать новый поток и завершать его в деструкторе.


#include <windows.h>
#include <process.h>

#define WM_SOMEFUNC   WM_USER+100

class CSomeSlass
{
public:
  CSomeClass();
  ~CSomeClass();

  // ...
  void SomeFunc(LPCSTR szSomeText) { PostThreadMessage(dwThreadID, WM_SOMEFUNC /* Ваше сообщение */, 0, (LPARAM) szSomeText); }
  // ...
private:
  static DWORD WINAPI ThreadFunc(LPVOID);

  HANDLE hThread;
  DWORD  dwThreadID;
};

CSomeClass::CSomeClass()
{
  HANDLE hEvent = CreateEvent(NULL, false, false, NULL);
  hThread = _beginthreadex(NULL, 100000 /* размер heap'а */ , ThreadFunc, hEvent, 0, &dwThreadID);
  if(WaitForSingleObject(hEvent, -1 /* бесконечное время, возможно стоит заменить на конкретную величину */) != WAIT_OBJECT_0)
  {
     // thread не успел запуститься за отведенное время
     // надо бы обработать такую "ошибку"
  }
  CloseHandle(hEvent);
  // ....
}

CSomeClass::~CSomeClass()
{
  if (hThread != NULL)
  {
    PostThreadMessage(dwThreadID, WM_QUIT, 0, 0);
    if(WaitForSingleObject(hThread, 2000 /* время ожидания реакции thread'а на просьбу завершить работу */) != WAIT_OBJECT_0)
    {
      // обработка невозможности корректно завершить thread
    }
    CloseHandle(hThread);
  }
  // ...
}

DWORD WINAPI CSomeClass::ThreadFunc(LPVOID hEvent)
{
  // ... 
  MSG msg;

  SetEvent(hEvent);
  while (true)
  {
    if(PeekMessage(&msg, (HWND)-1, 0, 0, PM_REMOVE) != 0)
    {
      switch(msg.message)
      {
         case WM_SOMEFUNC:
           // ... Тело функции, параметры забираем из lParam (приводим к нужному типу (CSomeParam*)msg.lParam)
           break;
         case WM_QUIT:
           return 0;
      }
    }
    // ... код, что должен работать вне зависимости от внешнего мира
    Sleep(10 /* период отдыха */);
  }
  return 0;
}
Re: обьект живущий в отдельном потоке
От: Аноним  
Дата: 12.02.05 14:05
Оценка:
Здравствуйте, gid_vvp, Вы писали:

_>Hi, All.


_>Может немного не по теме форума...

_>Но я не знаю куда запостить этот вопрос.

_>Необходимо сделать так чтобы некий объект (класс) полностью жил в отдельном потоке, т.е. чтобы все вызовы функций данного класса происходили в контексте его потока, даже если они вызываются из других потоков.


_>Возможно немного запутанно...

_>Но надеюсь на помощь.

Сам буквально недавно такое реализовывал.
tarkil в общем идею описал.
Очень помогает для реализации boost::function и boost::bind
Re[3]: обьект живущий в отдельном потоке
От: tarkil Россия http://5209.copi.ru/
Дата: 12.02.05 14:06
Оценка:
Здравствуйте, gid_vvp, Вы писали:

Кстати, а если не секрет, для чего Вам понадобился такой объект? Идею живых объектов-то я понял, а вот области применения...

Это офтоп, так что лучше в приват, наверное Контакты у меня в профиле.
--
wbr, Peter Taran
Re: обьект живущий в отдельном потоке
От: gid_vvp  
Дата: 12.02.05 14:07
Оценка:
Забыл написать..

Нужно чтобы всё это было как можно кроссплатформеннее
Re[2]: обьект живущий в отдельном потоке
От: tarkil Россия http://5209.copi.ru/
Дата: 12.02.05 14:16
Оценка: -1
Ух, ты. Про то, что Винда сообщение умеет слать не только окну, но и потоку я и забыл. Но будет ли польза, учитывая, что такие сообщения принципиально асинхронны?

А вот выбирать сообщения с использованием Sleep... Руки отрывать! GetMessage для кого придуман?
--
wbr, Peter Taran
Re[3]: обьект живущий в отдельном потоке
От: remark Россия http://www.1024cores.net/
Дата: 12.02.05 14:19
Оценка:
Здравствуйте, gid_vvp, Вы писали:

_>Здравствуйте, tarkil, Вы писали:



T>>По всей видимости, Вам нужно то, что именуется "живыми объектами", есть такая концепция. C++ напрямую её не поддерживает, так что придётся делать руками.


T>>Идея такая: делаем реальный класс объекта FooImpl и некий класс-оболочку, которая будет доступна из других потоков — Foo. FooImpl никто не видит, все пользуются Foo, хранящий указатель на FooImpl (короче, это обычная концепция объект+интерфейс).


T>>Ещё нам понадобится очередь, хранящая все обращения к FooImpl. Соответственно, поток, принадлежащий FooImpl (создаётся и грохается вместе с ним) делает одну простую вещь (здесь и далее используется псевдокод):


_>Спасибо, я нечто подобное себе и представлял.


_>Может быть у кого-нибудь есть ещё какие-нибудь варианты?


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

1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[3]: обьект живущий в отдельном потоке
От: ABK Украина  
Дата: 12.02.05 14:29
Оценка:
Здравствуйте, tarkil, Вы писали:

T>А вот выбирать сообщения с использованием Sleep... Руки отрывать! GetMessage для кого придуман?


Если возникла необходимость вынесения функциональности объекта в отдельный thread, то невольно возникает подозрение, что даже когда методы объекта не вызываются, он чего-то там делает.... И вот-это чего-то, которое выполняется всегда, не должно съедать всё процессорное время, поэтому Sleep мог бы ограничивать нагрузку объекта на процессор.

А насчёт использования GetMessage и PeekMessage — это всё зависит от контекста, когда это нужно было мне, я реализовал как написал, а на универсальность никогда не претендовал, это просто пример (хотя и из жизни)...
Re[4]: обьект живущий в отдельном потоке
От: tarkil Россия http://5209.copi.ru/
Дата: 12.02.05 14:33
Оценка:
Здравствуйте, ABK, Вы писали:

ABK>А насчёт использования GetMessage и PeekMessage — это всё зависит от контекста, когда это нужно было мне, я реализовал как написал, а на универсальность никогда не претендовал, это просто пример (хотя и из жизни)...


Да я так, шутя ругаюсь Я так тоже иногда пишу для простоты.
--
wbr, Peter Taran
Re[2]: обьект живущий в отдельном потоке
От: Аноним  
Дата: 12.02.05 15:57
Оценка:
Решение, основанное на сообщениях, как минимум платформенно зависимо.
Лучше использовать платформенно-независивые средства.

Метод и параметры к нему можно элегантно
упаковать с помощью boost::bind или аналогов
и тем самым полностью избавиться от switch/case блока в ThreadFunc.
Re: active object pattern
От: MaximE Великобритания  
Дата: 12.02.05 20:07
Оценка: 17 (6)
gid_vvp wrote:

> Необходимо сделать так чтобы некий объект (класс) полностью жил в отдельном потоке, т.е. чтобы все вызовы функций данного класса происходили в контексте его потока, даже если они вызываются из других потоков.


Паттерн называется active object, можешь google по этой фразе.

Основная сложность реализации заключается в передаче аргументов асинхронного вызова и обратно результата вызова между потоками.

Если аргументы и результат передаются по ссылке (указателю) то необходимо гарантировать, что объект, на который ссылаются, не был разрушен до того момента, как его начнут использовать в другом потоке. Проше всего такую гарантию обеспечить при помощи умных указателей с семантикой shared ownership, как, например, boost::shared_ptr или boost::intrusive_ptr.

boost содержит достаточно примитивов, чтобы быстренько набросать портабельную реализацию active object. Нам понадобятся thread, function, bind.

Классы поддержки:
////////////////////////////////////////////////////////////////////////////////////////////////

#include <queue>

#include "boost/thread/thread.hpp"
#include "boost/thread/mutex.hpp"
#include "boost/thread/condition.hpp"
#include "boost/thread/xtime.hpp"
#include "libs/thread/src/timeconv.inl"

#include "boost/function.hpp"
#include "boost/bind.hpp"

////////////////////////////////////////////////////////////////////////////////////////////////

void sleep(int ms)
{
     boost::xtime delay;
     to_time(ms, delay);
     boost::thread().sleep(delay);
}

////////////////////////////////////////////////////////////////////////////////////////////////

class worker_thread : private boost::noncopyable
{
public:
     typedef boost::function<void()> job;

public:
     worker_thread()
         : stop_(false)
         , thr_(boost::bind(&worker_thread::thread_fun, this))
     {}

     ~worker_thread()
     {
         {
             boost::mutex::scoped_lock l(mtx_);
             stop_ = true;
         }
         cnd_.notify_one();
         thr_.join();
     }

     void queue(job const& j)
     {
         {
             boost::mutex::scoped_lock l(mtx_);
             jobs_.push(j);
         }
         cnd_.notify_one();
     }

private:
     void thread_fun()
     {
         job j;
         while(true)
         {
             {
                 boost::mutex::scoped_lock l(mtx_);

                 while(!stop_ && jobs_.empty())
                     cnd_.wait(l);
                 if(stop_)
                     break;

                 jobs_.front().swap(j);
                 jobs_.pop();
             }
             j();
         }
     }

private:
     boost::mutex mtx_;
     boost::condition cnd_;
     std::queue<job> jobs_;

     bool stop_;
     boost::thread thr_;
};

////////////////////////////////////////////////////////////////////////////////////////////////

template<class T>
class async_result : private boost::noncopyable
{
public:
     async_result(T const& t = T())
         : result_(t)
         , ready_(false)
     {}

     ~async_result()
     {
         static_cast<void>(this->result()); // wait till it has been used by an active object
     }

     void set(T const& t)
     {
         {
             boost::mutex::scoped_lock l(mtx_);
             result_ = t;
             ready_ = true;
         }
         cnd_.notify_all();
     }

     bool ready()
     {
         boost::mutex::scoped_lock l(mtx_);
         return ready_;
     }

     T result()
     {
         boost::mutex::scoped_lock l(mtx_);
         while(!ready_)
             cnd_.wait(l);
         return result_;
     }

private:
     boost::mutex mtx_;
     boost::condition cnd_;
     T result_;
     bool ready_;
};

////////////////////////////////////////////////////////////////////////////////////////////////

template<class T>
struct active_object : private boost::noncopyable
{
     active_object(T const& t = T())
         : object(t)
     {}

     T object;
     worker_thread thread;
};

////////////////////////////////////////////////////////////////////////////////////////////////


Использование:
struct some
{
     void foo() { printf("%s\n", __FUNCTION__); sleep(1000); }
     int bar(int n) { printf("%s\n", __FUNCTION__); sleep(1000); return n * 2; }
};

struct some_proxy
{
     some_proxy(active_object<some>& a)
         : a_(&a)
     {}

     void foo()
     {
         a_->thread.queue(boost::bind(&some::foo, &a_->object));
     }

     void bar(int n, async_result<int>* r)
     {
         a_->thread.queue(boost::bind(&async_result<int>::set, r, boost::bind(&some::bar, &a_->object, n)));
     }

     active_object<some>* a_;
};

int main()
{
     active_object<some> a;
     some_proxy p(a);

     printf("calling foo\n");
     p.foo();

     async_result<int> r;
     printf("calling bar\n");
     p.bar(1, &r);
     printf("calling bar done: %d\n", r.result());
}


Небольшая проблема связана с реализацией прокси объектов: руками не очень хочется дублировать интерфейс active object, но просто по данному определению класса существующими средствами языка и препроцессора сгенерить реализацию прокси затруднительно. Как вариант, можно избавиться от прокси объектов вообще и нагенерить несколько перегрузок ф-ций упаковки аргументов вызова с разным числом параметров, как для boost::bind. Тогда синтаксис вызова будет выглядеть так:

template<class T, class F, class R, class A1>
void async_call(active_object<T>& a, F f, A1 a1, async_result<R>* r)
{
     a.thread.queue(boost::bind(&async_result<int>::set, r, boost::bind(f, &a.object, a1)));
}

//...

     async_result<int> s;
     printf("calling bar\n");
     async_call(a, &some::bar, 2, &s);
     printf("calling bar done: %d\n", s.result());


--
Maxim Yegorushkin

Those who do not understand Unix are condemned to reinvent it, poorly. © Henry Spencer
Posted via RSDN NNTP Server 1.9
Re: обьект живущий в отдельном потоке
От: ilejn Россия  
Дата: 14.02.05 08:25
Оценка: 4 (1)
Здравствуйте, gid_vvp, Вы писали:


_>Необходимо сделать так чтобы некий объект (класс) полностью жил в отдельном потоке, т.е. чтобы все вызовы функций данного класса происходили в контексте его потока, даже если они вызываются из других потоков.


Существует некая развесистая штучка под названием ACE
http://www.cs.wustl.edu/~schmidt/ACE.html

У меня нет собственного опыта ее использования и я бы
с удовольствием узнал о личных впечатлениях, если они есть
у кого-нибудь их присутствующих.
Re[2]: обьект живущий в отдельном потоке
От: MaximE Великобритания  
Дата: 14.02.05 10:13
Оценка: +1
ilejn wrote:

> Существует некая развесистая штучка под названием ACE

> http://www.cs.wustl.edu/~schmidt/ACE.html
>
> У меня нет собственного опыта ее использования и я бы
> с удовольствием узнал о личных впечатлениях, если они есть
> у кого-нибудь их присутствующих.

Старая монстроидальная библиотека, хотя работающая и применяющаяся в реальных проектах. Написана на С с классами, страдает от собственного наследия.

--
Maxim Yegorushkin

Those who do not understand Unix are condemned to reinvent it, poorly. © Henry Spencer
Posted via RSDN NNTP Server 1.9
Re[3]: обьект живущий в отдельном потоке
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 14.02.05 12:00
Оценка:
Здравствуйте, MaximE, Вы писали:

ME>ilejn wrote:


>> Существует некая развесистая штучка под названием ACE

>> http://www.cs.wustl.edu/~schmidt/ACE.html
>>
>> У меня нет собственного опыта ее использования и я бы
>> с удовольствием узнал о личных впечатлениях, если они есть
>> у кого-нибудь их присутствующих.

ME>Старая монстроидальная библиотека, хотя работающая и применяющаяся в реальных проектах. Написана на С с классами, страдает от собственного наследия.


От собственного наследия со временем начинают страдать все объектные библиотеки, вне зависимости от языка программирования

Сам ACE не такой уж монстрообразный, если изучать его не по исходным текстам, а по книгам или статьям его разработчиков. Например, есть три книги:

C++ Network Programming, Volume 1: Mastering Complexity with ACE and Patterns.
C++ Network Programming, Volume 2: Systematic Reuse with ACE and Frameworks.
ACE Programmer's Guide, The: Practical Design Patterns for Network and Systems Programming.

Две первые из них уже переведены на русский язык (хотя я их нашел в английском варианте через eDonkey).

Так вот системность и упорядоченность ACE начинает проглядываться еще при изучении C++NP v1, а в C++NP v2 все расставляется на свои места. Хотя нужно понимать, что ACE берет на себя решение только органиченного круга задач: например, работа с разными IPC-механизмами, таймерами, примитивами синхронизации, потоками и процессами. Т.е. через ACE можно организовывать "низкий уровень" приложения. А для прикладной логики придется все равно свои решения придумывать. Хотя в ACE и есть для этого какие-то инструменты типа ACE_Message_Queue.

Так же нужно понимать, что не все из ACE следует брать и использовать. Например, в ACE есть целая куча собственных контейнеров. Если есть возможность использовать STL и boost, то на них можно совсем не обращать внимания. А если приходится использовать платформу, где ни STL, ни boost-а нет, и компилятор не совсем стандарту соответствует, то почему бы ACE-вкими контейнерами не воспользоваться? Да и объем ACE после компиляции не большой: в VC++7.1 размер DLL-ки в release-режиме ~900Kb. А при использовании варианта в статической библиотеки увеличение размера exe-шника может быть в пределах ~500Kb, а то и меньше.

Сам я еще только в стадии изучения ACE нахожусь, еще основные framework-и из ACE (типа Reactor или Proactor) не использовал -- не дошли руки. Но есть большая надежда применить, например, средства для IPC-взаимодействия из ACE в своих проектах.
... << RSDN@Home 1.1.4 beta 3 rev. 185>>


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re[4]: обьект живущий в отдельном потоке
От: MaximE Великобритания  
Дата: 14.02.05 12:18
Оценка:
eao197 wrote:

> Так же нужно понимать, что не все из ACE следует брать и использовать. Например, в ACE есть целая куча собственных контейнеров. Если есть возможность использовать STL и boost, то на них можно совсем не обращать внимания. А если приходится использовать платформу, где ни STL, ни boost-а нет, и компилятор не совсем стандарту соответствует, то почему бы ACE-вкими контейнерами не воспользоваться? Да и объем ACE после компиляции не большой: в VC++7.1 размер DLL-ки в release-режиме ~900Kb. А при использовании варианта в статической библиотеки увеличение размера exe-шника может быть в пределах ~500Kb, а то и меньше.


Я забыл явно написать, что вышенаписанное было лично моим мнением. Я не спорю, что ACE работает, и, возможно, что-то упрощает. Когда я с ним сталкивался пару лет назад, мне не понравились следующие вещи:


--
Maxim Yegorushkin

Those who do not understand Unix are condemned to reinvent it, poorly. © Henry Spencer
Posted via RSDN NNTP Server 1.9
Re[5]: обьект живущий в отдельном потоке
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 14.02.05 12:40
Оценка:
Здравствуйте, MaximE, Вы писали:

ME>Я забыл явно написать, что вышенаписанное было лично моим мнением. Я не спорю, что ACE работает, и, возможно, что-то упрощает. Когда я с ним сталкивался пару лет назад, мне не понравились следующие вещи:


Ну, то что я сказал, так же было моим личным мнением

ME>Naming conventions и coding style мне были совсем не по вкусу. Я понимаю, что это все не от хорошей жизни, а от того, что библиотека существует то ли с 1991г, то ли..., когда были компиляторы не поддерживающие пространства имен, не было стандартной библиотеки с контейнерами, etc..


Да, naming conventions там своеобразный. Но т.к. он есть, то к нему быстро привыкаешь. Даже удобно становится, т.к. сразу видно, что из ACE, а что свое

ME>Библиотека позиционируется как кроссплатформенная, но в хедерах я находил, что куски интерфейсов классов вырубались #ifdef платформа.


Это, наверное, от того, что не все фишки на всех платформах можно реализовать вообще или реализовать их с достаточной степенью эффективности. Поэтому разработчики ACE пошли по такому пути, чтобы не реализуемую на данной платформе функциональность вообще отключить. И чтобы использующее ACE приложение это понимало. Об этом в "C++ Network Programming, Volume 1" довольно часто говорится.
... << RSDN@Home 1.1.4 beta 3 rev. 185>>


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re[2]: active object pattern
От: Tom Россия http://www.RSDN.ru
Дата: 14.02.05 16:59
Оценка:
Хочу ещё добавить, что проблемма сильно усложняется, если обьект живущий в потоке — не синглтон.
Народная мудрось
всем все никому ничего(с).
Re: обьект живущий в отдельном потоке
От: vvaizh http://izh-test.sourceforge.net/
Дата: 14.02.05 17:17
Оценка:
Здравствуйте, gid_vvp, Вы писали:

_>Hi, All.


_>Может немного не по теме форума...

_>Но я не знаю куда запостить этот вопрос.

_>Необходимо сделать так чтобы некий объект (класс) полностью жил в отдельном потоке, т.е. чтобы все вызовы функций данного класса происходили в контексте его потока, даже если они вызываются из других потоков.


_>Возможно немного запутанно...

_>Но надеюсь на помощь.

Собственно проблема сводится к реализации "сопросграммы"
идея описана где то у Хора..
сводится к реализации одного метода.. (вместо нескольких)
и передаче параметров для него через переменные как описано у АВК
вот тут у меня готовый классик рабочий с текстом
(как есть, то есть сильно грязный, но зато протестированный
и вполне рабочий)

Взамен — хочу ответ на вопрос, где вы хотите это применить?
(
а) состояние графического редактора
б) контекст сессии для web-сервера
в) синтаксический парсер
г) что то новое..
)

могу докидать собственных публикаций на тему, если интересно..

///////////////////////////////////////////////////////////////////////////////
// subprogram.h by vva
///////////////////////////////////////////////////////////////////////////////

#ifndef _subprogram_h
#define _subprogram_h

#ifdef WIN32

#pragma warning(disable:4786)
#pragma warning(disable:4514)
#pragma warning(push, 1)
# include <map>
#pragma warning(pop)
#pragma warning(push, 4)
#pragma warning(disable:4786)

//# include "windows.h"
#define _DWORD unsigned long
#define _HANDLE void *
#define _WINAPI __stdcall
#define _LPVOID void *

#endif

namespace izh {

class Sub_program;

enum sub_program_petition{please_finish,no_petition};

///////////////////////////////////////////////////////////////////////////////
class Sub_program_runner
{
public:
Sub_program_runner ();
~Sub_program_runner ();

bool has_control_stream ();
bool is_started_not_finished ();
bool is_waited_sub_program ();

bool start (Sub_program* sp, sub_program_petition p= no_petition);
bool execute_portion( sub_program_petition p= no_petition);
void finish ();

void lock_for_execute ();
void unlock_for_execute ();

Sub_program *get_sub_program();

static bool is_root_process ();
static Sub_program_runner *get_current_sub_program ();
private:
Sub_program *sub_program;
friend class Sub_program;

#ifdef WIN32
_DWORD subprogram_thread_id;
_DWORD caller_thread_id;
_HANDLE event_may_execute_portion;
_HANDLE event_may_continue_caller;
void *critical_section_execute;

void on_close_sub_program();
#endif // WIN32
};
///////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////
class Sub_program
{
public:
Sub_program(){};
virtual ~Sub_program(){};

Sub_program_runner *get_caller(){return caller;};
protected:

virtual void main(sub_program_petition){};

private:
friend class Sub_program_runner;
#ifdef WIN32
static _DWORD _WINAPI Sub_program_ThreadProc(_LPVOID lpParameter);
#endif // WIN32

Sub_program_runner *caller;
};
///////////////////////////////////////////////////////////////////////////////

#ifdef WIN32

#undef _DWORD
#undef _HANDLE
#undef _WINAPI
#undef _LPVOID

#endif

}

#endif // _subprogram_h

///////////////////////////////////////////////////////////////////////////////


///////////////////////////////////////////////////////////////////////////////
// win_subprogram.cpp by vva
///////////////////////////////////////////////////////////////////////////////

#pragma warning(disable:4786)
#include "windows.h"
#include "Winbase.h"

//#define WITH_TRACE

#include "subprogram.h"

#define _DWORD unsigned long
#define _HANDLE void *
#define _WINAPI __stdcall
#define _LPVOID void *

typedef std::map<_DWORD,izh::Sub_program_runner*> map;
static map sub_programs;

using namespace izh;

///////////////////////////////////////////////////////////////////////////////
// implementation for Sub_program_runner:
///////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////
DWORD Sub_program::Sub_program_ThreadProc(LPVOID lpParameter)
{
#ifdef WITH_TRACE
printf("start subprogram\n");
#endif

Sub_program_runner *sub_program_runner= (Sub_program_runner *)lpParameter;
sub_programs.
insert(map::value_type( GetCurrentThreadId(), sub_program_runner));
WaitForSingleObject(sub_program_runner->event_may_execute_portion,INFINITE);

sub_program_runner->get_sub_program()->main(no_petition);

sub_program_runner->on_close_sub_program();

#ifdef WITH_TRACE
printf("finish subprogram\n");
#endif

return 0;
}

///////////////////////////////////////////////////////////////////////////////
Sub_program_runner::Sub_program_runner()
{
#ifdef WITH_TRACE
printf("Sub_program_runner::Sub_program_runner %p\n",this);
#endif
sub_program= 0;
subprogram_thread_id= 0;
critical_section_execute= new CRITICAL_SECTION;
InitializeCriticalSection((CRITICAL_SECTION*)critical_section_execute);
}

///////////////////////////////////////////////////////////////////////////////
Sub_program_runner::~Sub_program_runner()
{
#ifdef WITH_TRACE
printf("Sub_program_runner::~Sub_program_runner %p\n",this);
#endif
if (is_started_not_finished() && !execute_portion(please_finish))
finish();
DeleteCriticalSection((CRITICAL_SECTION*)critical_section_execute);
delete (CRITICAL_SECTION*)critical_section_execute;
}

///////////////////////////////////////////////////////////////////////////////
void Sub_program_runner::lock_for_execute()
{
if (get_current_sub_program()!=this)
EnterCriticalSection((CRITICAL_SECTION*)critical_section_execute);
}

///////////////////////////////////////////////////////////////////////////////
void Sub_program_runner::unlock_for_execute()
{
if (get_current_sub_program()!=this)
LeaveCriticalSection((CRITICAL_SECTION*)critical_section_execute);
}

///////////////////////////////////////////////////////////////////////////////
void Sub_program_runner::on_close_sub_program()
{
subprogram_thread_id= 0;
sub_program= 0;
sub_programs.erase(GetCurrentThreadId());
SetEvent(event_may_continue_caller);
}

///////////////////////////////////////////////////////////////////////////////
Sub_program *Sub_program_runner::get_sub_program()
{
return sub_program;
}

///////////////////////////////////////////////////////////////////////////////
bool Sub_program_runner::has_control_stream()
{
return false;
}

///////////////////////////////////////////////////////////////////////////////
bool Sub_program_runner::is_waited_sub_program()
{
return false;
}

///////////////////////////////////////////////////////////////////////////////
bool Sub_program_runner::is_started_not_finished()
{
return subprogram_thread_id!= 0;
}

///////////////////////////////////////////////////////////////////////////////
bool Sub_program_runner::start(Sub_program* sp, sub_program_petition)
{
if (is_started_not_finished())
return false;
sub_program= sp;
sp->caller= get_current_sub_program();
event_may_continue_caller= CreateEvent(NULL,true,false,NULL);
event_may_execute_portion= CreateEvent(NULL,true,false,NULL);
caller_thread_id= GetCurrentThreadId();
CreateThread(NULL,0,Sub_program::Sub_program_ThreadProc,
this,0,&subprogram_thread_id);
SetEvent(event_may_execute_portion);
WaitForSingleObject(event_may_continue_caller,INFINITE);
return false;
}

///////////////////////////////////////////////////////////////////////////////
/*
SYNOPSIS
Sub_program_runner::execute_portion(sub_program_petition p)
p — please_finish|no_petition — petition for subprogram
DESCRIPTION
Test if a second key is the subkey of the first one.
NOTE
let subprogram to execute next portion of code (wait for finish of portion)
RETURN
true — succeseful
false — unsucceseful
*/
bool Sub_program_runner::execute_portion(sub_program_petition)
{
map::iterator res= sub_programs.find(GetCurrentThreadId());

if (res->second==this) return false;

if (res==sub_programs.end()) // root thread
{
if (!this) // can not call root thread from root thread
return false;
ResetEvent(event_may_continue_caller);
SetEvent(event_may_execute_portion);
WaitForSingleObject(event_may_continue_caller,INFINITE);
return true;
}

Sub_program_runner *cursp= res->second;

if (this && !is_started_not_finished()) return false;

if (cursp->get_sub_program()->get_caller()==this)
{
ResetEvent(cursp->event_may_execute_portion);
SetEvent(cursp->event_may_continue_caller);
WaitForSingleObject(cursp->event_may_execute_portion,INFINITE);
return true;
}

ResetEvent(event_may_continue_caller);
SetEvent(event_may_execute_portion);
WaitForSingleObject(event_may_continue_caller,INFINITE);
return true;
}

///////////////////////////////////////////////////////////////////////////////
void Sub_program_runner::finish()
{
/*HANDLE hthread= OpenThread(THREAD_TERMINATE,TRUE,subprogram_thread_id);
TerminateThread(hthread,0);
sub_program= 0;
subprogram_thread_id= 0;*/
}

///////////////////////////////////////////////////////////////////////////////
bool Sub_program_runner::is_root_process()
{
map::iterator res= sub_programs.find(GetCurrentThreadId());
return res==sub_programs.end();
}

///////////////////////////////////////////////////////////////////////////////
Sub_program_runner *Sub_program_runner::get_current_sub_program()
{
map::iterator res= sub_programs.find(GetCurrentThreadId());
return res==sub_programs.end() ? 0 : res->second;
}


///////////////////////////////////////////////////////////////////////////////


///////////////////////////////////////////////////////////////////////////////
// sub_program_test.cpp by vva
///////////////////////////////////////////////////////////////////////////////

#include "windows.h"
#include "subprogram.h"

using namespace izh;

///////////////////////////////////////////////////////////////////////////////
class test_Sub_program : public Sub_program
{
public:
int depth, max_depth;
test_Sub_program(int adepth= 0, int amax_depth= 4);
void main(sub_program_petition p= no_petition);

void print(const char *txt);
};
///////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////
test_Sub_program::test_Sub_program(int adepth, int amax_depth)
{
depth=adepth;
max_depth= amax_depth;
}

///////////////////////////////////////////////////////////////////////////////
void test_Sub_program::print(const char *txt)
{
for (int i=0; i<=depth; i++)
printf(" ");
printf("test_Sub_program::main(%d)\t%s\n",depth,txt);
}

///////////////////////////////////////////////////////////////////////////////
void test_Sub_program::main(sub_program_petition /*p*/)
{
print("begin");

if (depth==max_depth){
print("return");
return;
}

Sub_program_runner child_runner;
test_Sub_program child(depth+1,max_depth);

print("get_caller()->execute_portion()");
get_caller()->execute_portion();

print("child_runner.start(&child)");
child_runner.start(&child);

while (child_runner.is_started_not_finished())
{
print("get_caller()->execute_portion()");
get_caller()->execute_portion();

print("child_runner.execute_portion()");
child_runner.execute_portion();
}

print("end");
}

///////////////////////////////////////////////////////////////////////////////
void PrintRunnerParameters(Sub_program_runner *runner)
{
printf("-------\n");
printf("is_started_not_finished()= %d\n",runner->is_started_not_finished());
printf("is_started_not_finished()= %d\n",runner->is_waited_sub_program());
printf("has_control_stream()= %d\n",runner->has_control_stream());
printf("-------\n");
}

///////////////////////////////////////////////////////////////////////////////
int main()
{
printf("main begin\n");
Sub_program_runner test_runner;
test_Sub_program test_subprogram(0,8);

PrintRunnerParameters(&test_runner);

printf("start : ++++++++++\n");

test_runner.start(&test_subprogram);
PrintRunnerParameters(&test_runner);
printf("main loop : \n");
while (test_runner.is_started_not_finished())
{
PrintRunnerParameters(&test_runner);
printf("main : next portion {\n");
test_runner.execute_portion();
printf("}\n");
}
printf("finish ++++++++++\n");

PrintRunnerParameters(&test_runner);

//fgetchar();

return 0;
}

///////////////////////////////////////////////////////////////////////////////
http://izh-test.sourceforge.net/russian/introduction.html
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.