Может немного не по теме форума...
Но я не знаю куда запостить этот вопрос.
Необходимо сделать так чтобы некий объект (класс) полностью жил в отдельном потоке, т.е. чтобы все вызовы функций данного класса происходили в контексте его потока, даже если они вызываются из других потоков.
Возможно немного запутанно...
Но надеюсь на помощь.
Здравствуйте, gid_vvp, Вы писали:
_>Необходимо сделать так чтобы некий объект (класс) полностью жил в отдельном потоке, т.е. чтобы все вызовы функций данного класса происходили в контексте его потока, даже если они вызываются из других потоков.
По всей видимости, Вам нужно то, что именуется "живыми объектами", есть такая концепция. C++ напрямую её не поддерживает, так что придётся делать руками.
Идея такая: делаем реальный класс объекта FooImpl и некий класс-оболочку, которая будет доступна из других потоков — Foo. FooImpl никто не видит, все пользуются Foo, хранящий указатель на FooImpl (короче, это обычная концепция объект+интерфейс).
Ещё нам понадобится очередь, хранящая все обращения к FooImpl. Соответственно, поток, принадлежащий FooImpl (создаётся и грохается вместе с ним) делает одну простую вещь (здесь и далее используется псевдокод):
Очередь должна позаботиться, чтобы в MakeCall вызывающий поток подвис в ожидании, зато дождалась бы функция WaitForCall и исполнила запрос, после чего следующий WaitForCall снова б стал ждать, а MakeCall вернула управление. Это не то, чтобы совсем просто, но и не сложно сделать на объектах синхронизации Винды.
Самый затык это придумать класс Call (и сопутствующий типобезопасный класс CallImpl), инкапсулирующий в себя аргументы, передаваемые методу, возвращаемое значение и, собственно, какой-то идентификатор метода. Аналог IDispatch, короче, в его сторону я б и смотрел. Или у Александреску был пример разработки чего-то похожего в Loki.
Как сделать без такой сложной инкапсуляции вызова я не знаю, скорее всего никак.
T>По всей видимости, Вам нужно то, что именуется "живыми объектами", есть такая концепция. C++ напрямую её не поддерживает, так что придётся делать руками.
T>Идея такая: делаем реальный класс объекта FooImpl и некий класс-оболочку, которая будет доступна из других потоков — Foo. FooImpl никто не видит, все пользуются Foo, хранящий указатель на FooImpl (короче, это обычная концепция объект+интерфейс).
T>Ещё нам понадобится очередь, хранящая все обращения к FooImpl. Соответственно, поток, принадлежащий FooImpl (создаётся и грохается вместе с ним) делает одну простую вещь (здесь и далее используется псевдокод):
Спасибо, я нечто подобное себе и представлял.
Может быть у кого-нибудь есть ещё какие-нибудь варианты?
Здравствуйте, 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
Ух, ты. Про то, что Винда сообщение умеет слать не только окну, но и потоку я и забыл. Но будет ли польза, учитывая, что такие сообщения принципиально асинхронны?
А вот выбирать сообщения с использованием Sleep... Руки отрывать! GetMessage для кого придуман?
Здравствуйте, gid_vvp, Вы писали:
_>Здравствуйте, tarkil, Вы писали:
T>>По всей видимости, Вам нужно то, что именуется "живыми объектами", есть такая концепция. C++ напрямую её не поддерживает, так что придётся делать руками.
T>>Идея такая: делаем реальный класс объекта FooImpl и некий класс-оболочку, которая будет доступна из других потоков — Foo. FooImpl никто не видит, все пользуются Foo, хранящий указатель на FooImpl (короче, это обычная концепция объект+интерфейс).
T>>Ещё нам понадобится очередь, хранящая все обращения к FooImpl. Соответственно, поток, принадлежащий FooImpl (создаётся и грохается вместе с ним) делает одну простую вещь (здесь и далее используется псевдокод):
_>Спасибо, я нечто подобное себе и представлял.
_>Может быть у кого-нибудь есть ещё какие-нибудь варианты?
Можно ещё так: "живой объект" создаёт для себя очередь событий (pipe) и ждёт, когда кто-нибудь в неё что-нибудь запишет. Клиенты кладут в эту очередь сообщения определенного формата, после этого "живой объект" просыпается и обрабатывает сообщение. Т.о., общающиеся объекты "развязаны" и могут жить в разных потоках, общаясь через общую очередь.
Здравствуйте, tarkil, Вы писали:
T>А вот выбирать сообщения с использованием Sleep... Руки отрывать! GetMessage для кого придуман?
Если возникла необходимость вынесения функциональности объекта в отдельный thread, то невольно возникает подозрение, что даже когда методы объекта не вызываются, он чего-то там делает.... И вот-это чего-то, которое выполняется всегда, не должно съедать всё процессорное время, поэтому Sleep мог бы ограничивать нагрузку объекта на процессор.
А насчёт использования GetMessage и PeekMessage — это всё зависит от контекста, когда это нужно было мне, я реализовал как написал, а на универсальность никогда не претендовал, это просто пример (хотя и из жизни)...
Здравствуйте, ABK, Вы писали:
ABK>А насчёт использования GetMessage и PeekMessage — это всё зависит от контекста, когда это нужно было мне, я реализовал как написал, а на универсальность никогда не претендовал, это просто пример (хотя и из жизни)...
Да я так, шутя ругаюсь Я так тоже иногда пишу для простоты.
--
wbr, Peter Taran
Re[2]: обьект живущий в отдельном потоке
От:
Аноним
Дата:
12.02.05 15:57
Оценка:
Решение, основанное на сообщениях, как минимум платформенно зависимо.
Лучше использовать платформенно-независивые средства.
Метод и параметры к нему можно элегантно
упаковать с помощью boost::bind или аналогов
и тем самым полностью избавиться от switch/case блока в ThreadFunc.
gid_vvp wrote:
> Необходимо сделать так чтобы некий объект (класс) полностью жил в отдельном потоке, т.е. чтобы все вызовы функций данного класса происходили в контексте его потока, даже если они вызываются из других потоков.
Паттерн называется active object, можешь google по этой фразе.
Основная сложность реализации заключается в передаче аргументов асинхронного вызова и обратно результата вызова между потоками.
Если аргументы и результат передаются по ссылке (указателю) то необходимо гарантировать, что объект, на который ссылаются, не был разрушен до того момента, как его начнут использовать в другом потоке. Проше всего такую гарантию обеспечить при помощи умных указателей с семантикой shared ownership, как, например, boost::shared_ptr или boost::intrusive_ptr.
boost содержит достаточно примитивов, чтобы быстренько набросать портабельную реализацию active object. Нам понадобятся thread, function, bind.
Небольшая проблема связана с реализацией прокси объектов: руками не очень хочется дублировать интерфейс 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());
_>Необходимо сделать так чтобы некий объект (класс) полностью жил в отдельном потоке, т.е. чтобы все вызовы функций данного класса происходили в контексте его потока, даже если они вызываются из других потоков.
ilejn wrote:
> Существует некая развесистая штучка под названием ACE > http://www.cs.wustl.edu/~schmidt/ACE.html > > У меня нет собственного опыта ее использования и я бы > с удовольствием узнал о личных впечатлениях, если они есть > у кого-нибудь их присутствующих.
Старая монстроидальная библиотека, хотя работающая и применяющаяся в реальных проектах. Написана на С с классами, страдает от собственного наследия.
Здравствуйте, 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++.
eao197 wrote:
> Так же нужно понимать, что не все из ACE следует брать и использовать. Например, в ACE есть целая куча собственных контейнеров. Если есть возможность использовать STL и boost, то на них можно совсем не обращать внимания. А если приходится использовать платформу, где ни STL, ни boost-а нет, и компилятор не совсем стандарту соответствует, то почему бы ACE-вкими контейнерами не воспользоваться? Да и объем ACE после компиляции не большой: в VC++7.1 размер DLL-ки в release-режиме ~900Kb. А при использовании варианта в статической библиотеки увеличение размера exe-шника может быть в пределах ~500Kb, а то и меньше.
Я забыл явно написать, что вышенаписанное было лично моим мнением. Я не спорю, что ACE работает, и, возможно, что-то упрощает. Когда я с ним сталкивался пару лет назад, мне не понравились следующие вещи:
Naming conventions и coding style мне были совсем не по вкусу. Я понимаю, что это все не от хорошей жизни, а от того, что библиотека существует то ли с 1991г, то ли..., когда были компиляторы не поддерживающие пространства имен, не было стандартной библиотеки с контейнерами, etc..
Библиотека позиционируется как кроссплатформенная, но в хедерах я находил, что куски интерфейсов классов вырубались #ifdef платформа.
Здравствуйте, 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++.
Здравствуйте, gid_vvp, Вы писали:
_>Hi, All.
_>Может немного не по теме форума... _>Но я не знаю куда запостить этот вопрос.
_>Необходимо сделать так чтобы некий объект (класс) полностью жил в отдельном потоке, т.е. чтобы все вызовы функций данного класса происходили в контексте его потока, даже если они вызываются из других потоков.
_>Возможно немного запутанно... _>Но надеюсь на помощь.
Собственно проблема сводится к реализации "сопросграммы"
идея описана где то у Хора..
сводится к реализации одного метода.. (вместо нескольких)
и передаче параметров для него через переменные как описано у АВК
вот тут у меня готовый классик рабочий с текстом
(как есть, то есть сильно грязный, но зато протестированный
и вполне рабочий)
Взамен — хочу ответ на вопрос, где вы хотите это применить?
(
а) состояние графического редактора
б) контекст сессии для web-сервера
в) синтаксический парсер
г) что то новое..
)
могу докидать собственных публикаций на тему, если интересно..
///////////////////////////////////////////////////////////////////////////////
// subprogram.h by vva
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
/*
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;
}
Tom wrote:
> Хочу ещё добавить, что проблемма сильно усложняется, если обьект живущий в потоке — не синглтон.
Непонятно, что имеется ввиду под данным заявлением.
Возможные варианты:
Один объект, один поток. Код приводился, нет проблем.
Один объект, пул потоков. Методы объекта должны быть thread-safe, больше проблем нет.
Несколько объектов, один поток. Смысла большого нет, но реализуется в точности как п.1.
Несколько объектов, пул потоков. В точности, как п.2.
-- Maxim Yegorushkin
Недавно реализовывал подобную вещь как сервис/драйвер винды. Объект создаётся в процессе services.exe или driver.exe, все вызовы методов происходят через DeviceIoControl. В данном случае можно обойтись даже без мощного марашаллинга, поскольку на время вызова процесс объекта разделяет память вызывающего процесса. О, как.
В общем, работает весьма приятно, только с отладкой несколько сложновато.
Кстати, хотелось бы узнать, может есть уже какие-то отработанные технологии для использования объектов размещенных в сервисе, например на основе ATL ?
А, вот ещё: а почему вас собственно не устраивает COM Exe Server ?
Здравствуйте, Chez, Вы писали:
C>А, вот ещё: а почему вас собственно не устраивает COM Exe Server ?
А почему он собственно должен устраивать?
Он плохо подходит на эту роль.
Это решение плохо переносимо да и чтобы добиться нужного поведения
придется еще довольно много повозиться.
Пихать в приложение довольно толстую прослойку из COM
только ради активных объектов я бы не стал.
Ну и наконец все это довольно элегантно реализуется средствами С++
с помощью boost (см. пост от MaximE).
Здравствуйте, Tom, Вы писали:
Tom>Хочу ещё добавить, что проблемма сильно усложняется, если обьект живущий в потоке — не синглтон.
А почему это усложняет жизнь?
Вроде никаких проблем из-за "несинглтонности" нету.
Другое дело, если он является noncopyable
и есть желание копировать такие объекты направо и налево.
Тогда да, ситуация усложняется.
А создавать такие объекты с нуля можно без проблем в любом количестве.
Другое дело, если он НЕ является noncopyable
и есть желание копировать такие объекты направо и налево.
Re: обьект живущий в отдельном потоке
От:
Аноним
Дата:
15.02.05 09:38
Оценка:
Здравствуйте, gid_vvp, Вы писали:
_>Hi, All.
_>Может немного не по теме форума... _>Но я не знаю куда запостить этот вопрос.
_>Необходимо сделать так чтобы некий объект (класс) полностью жил в отдельном потоке, т.е. чтобы все вызовы функций данного класса происходили в контексте его потока, даже если они вызываются из других потоков.
_>Возможно немного запутанно... _>Но надеюсь на помощь.
Не задана операционная система, но для Windows надо вызывать функции через посылку сообщения потоку, в котором живет объект.
Именно так устороен объект модели Apartment технологии COM.
Поэтому можно использовать ATL для создания обьекта и его методов или даже Visual Basic 6.
Однако, в этом случае не избежать потерей в быстродействии из-за маршалинга.
Ы
Здравствуйте, vvaizh, Вы писали:
V>Взамен — хочу ответ на вопрос, где вы хотите это применить? V>( V>а) состояние графического редактора V>б) контекст сессии для web-сервера V>в) синтаксический парсер V>г) что то новое.. V>)
Пишу класс для работы с контроллером станка, а драйвера имеют
особенность: с ними можно работать только из одного потока, а
приложение подразумевается многопоточное.