Andrew S wrote:
> Надоело постоянно писать статические функции-прокси для вызова функций-членов класса в отдельном потоке, захотелось это дело как то автоматизировать.
Радикальное решение — boost::threads. В качестве бонуса получаешь все прелести boost::bind.
ME>>Радикальное решение — boost::threads. В качестве бонуса получаешь все прелести boost::bind.
AD>Лично мне концепция boost::threads кажется неудобной (по стилю ассоциации данных с потоками). Может быть потому, что я привык к потокам в стиле VCL
AD>Хотя конкретно для данной задачи boost::threads подходят идеально.
Потоки в стиле VCL — это ужасное решение. Да прибудет с ними гиена огненная, пусть сгинут во мраке ада. Аминь.
А по поводу boost::threads скажу лишь одно — не плодите сущности без необходимости. Но за совет спасибо, будет время — ознакомлюсь.
Надоело постоянно писать статические функции-прокси для вызова функций-членов класса в отдельном потоке, захотелось это дело как то автоматизировать. Вот такой код вполне валиден, однако на VC6 не компилится (точнее, не инстанцируется):
template <class T, int (T::*f)()>
struct CProxy
{
static int ThreadProcStub(T *p)
{
return (p->*f)();
}
};
class A
{
public:
int onA()
{
return 1;
};
int onB()
{
return 2;
};
};
int (*f)(A *) = &CProxy<A, &A::onA>::ThreadProcStub; // invalid template argument
На "правильных" компиляторах, конечно, все ОК. Есть ли какой обходной путь добиться такого (т.е. фактически генерации нужной статической функции на основе типа класса и адреса его функции-члена) на VC6?
Спасибо.
ПК>Т.е. сделать искуственный вложенный шаблон класса с одним параметром — ПК>указателем на член.
Верно... И ведь уже раньше так делал, да и пост этот тоже видел. День,наверное, не задался
Только вот до сих пор для меня загадка, в чем VC видит отличие между этими вариантами. Шаманство, в общем
Павел, большое спасибо!
On Thu, 22 Jan 2004 19:37:35 GMT, ArtDenis <15178@news.rsdn.ru> wrote:
> Здравствуйте, MaximE, Вы писали: > > ME>Радикальное решение — boost::threads. В качестве бонуса получаешь все прелести boost::bind. > > Лично мне концепция boost::threads кажется неудобной (по стилю ассоциации данных с потоками). Может быть потому, что я привык к потокам в стиле VCL
Непонятно "ассоциации данных с потоками". Объект потока не обязательно сохранять, т.к. разрушение этого объекта не завершает поток.
Здравствуйте, MaximE, Вы писали:
>> Лично мне концепция boost::threads кажется неудобной (по стилю ассоциации данных с потоками). Может быть потому, что я привык к потокам в стиле VCL ME>Непонятно "ассоциации данных с потоками".
Извиняюсь, слишком кратко написал. Представь себе, что ты пишешь многопоточный сервер. На одно подключение клиента — один поток. Для каждого потока — свой набор данных. В VCL это всё делалось элементарно: ты наследовался от класса потока, добавлял свои данные, а затем с ними работал. А как это сделано в boost::threads? Через thread_specific_ptr. Вот пример из буста:
AD>Извиняюсь, слишком кратко написал. Представь себе, что ты пишешь многопоточный сервер. На одно подключение клиента — один поток. Для каждого потока — свой набор данных. В VCL это всё делалось элементарно: ты наследовался от класса потока, добавлял свои данные, а затем с ними работал. А как это сделано в boost::threads? Через thread_specific_ptr. Вот пример из буста:
Здравствуйте, Andrew S, Вы писали:
AS>Всем доброго времени суток.
AS>Надоело постоянно писать статические функции-прокси для вызова функций-членов класса в отдельном потоке, захотелось это дело как то автоматизировать.
Почему бы не воспользоваться boost?
class thread_launcher
{
public:
typedef boost::function0<void> func_type;
private:
static int thunk(void* pv)
{
*pbf = (func_type*)pv;
(*pbf)();
delete pbf;
}
public:
static unsigned long launch(const func_type& fn)
{
return beginthread(thunk, 0, new func_type(fn));
}
template<class C>
static unsigned long launch(C* pobj, void(&C::method)())
{
return launch(func_type(pobj, method));
}
};
Перекуём баги на фичи!
Re[2]: member function as template argument (VC6) workaround
Да MaximE конечно красиво записал, но это не addresed to the problem.
Связь данных и потока так и остаётся за кадром.
Когда можно разрушить объект `С`? Из кода не ясно.
Данная схема позволяет запустить несколько параллельных потоков метода одного и того же объекта, что видимо далеко не всегда желаемо if at all.
Как я уже написал в предыдущем посте (правда забыл в инициализации привязаться через bind :-) различные схемы решения таких проблем вполне можно решить используя и бустовские классы.
Вопрос — стоило ли добавлять в boost такие обвязки?
Re[4]: member function as template argument (VC6) workaround
Здравствуйте, Xor., Вы писали:
X>Да MaximE конечно красиво записал, но это не addresed to the problem. X>Связь данных и потока так и остаётся за кадром. X>Когда можно разрушить объект `С`? Из кода не ясно.
Судя по всему, AndrewS сам контролирует жизнь объекта класса. В данном случае использование boost::threads позволяет отказаться от использования статических функций в классе для запуска потока.
Интересный код, я так и не понял, что он делает
X>PS. вообще-то я очень поверхностно знаю boost, поэтому возможно, что есть варианты и получше.
Конечно же есть. В моём верхнем сообщении я как раз привёл пример из буста с таким решением. Просто лично мне оно не нравится.
AD>Судя по всему, AndrewS сам контролирует жизнь объекта класса. В данном случае использование boost::threads позволяет отказаться от использования статических функций в классе для запуска потока.
Именно так. Более того, совершенно нежелательно ассоциировать экземпляр класса с потоком, т.к. потоков у одного экземпляра может быть и несколько, и они выполняют совершенно разные задачи. Судя по всему, реализовать это при помощи boost::threads очень даже можно, но так уж получается, что я предпочитаю не использовать высокоуровневые обертки там, где без этого можно обойтись. И бью других по рукам за излишнее усложнение кода там, где можно обойтись более простыми и архитектурно чистыми решениями.
Xor. wrote:
> Да MaximE конечно красиво записал, но это не addresed to the problem.
Насколько я понимаю, проблема была вызвать ф-цию член в отдельном потоке. boost::treads решает эту проблему. Или я что-то упускаю?
> Связь данных и потока так и остаётся за кадром.
Так и не понял смысл фразы "Связь данных и потока". Что это значит, что хорошо, что плохо?
> Когда можно разрушить объект `С`? Из кода не ясно.
Из кода ясно, что экземпляр класса C c — глобальный объект. Также ясно, что main() не вернет управления, пока не завершиться поток, направленный в функцию-член c.
> Данная схема позволяет запустить несколько параллельных потоков метода одного и того же объекта, что видимо далеко не всегда желаемо if at all.
Это преимущество данной схемы — не накладывает ограничений.
В ACE, например, чтобы направить поток в ф-цию-член, надо отнаследоваться от класса потока, и реализовать виртуальную ф-цию-член Run() (или что-то наподобие). Я если я хочу два потока в разные ф-ции-члены? Придется приложить усилия, чтобы сделать это при помощи такой схемы. (не говоря о том, что добавленние vtable в класс может быть не очень желательным)
> Как я уже написал в предыдущем посте (правда забыл в инициализации привязаться через bind различные схемы решения таких проблем вполне можно решить используя и бустовские классы. > > Вопрос — стоило ли добавлять в boost такие обвязки?
Какие именно "обвязки"? Если имеешь в виду boost::threads, то это не просто обвязки — эту мультиплатформенные обвязки (по крайней мере win32, POSIX, MAC), которые часто позволяют не опускаться до уровня API платформы.
Andrew S wrote:
> AD>Судя по всему, AndrewS сам контролирует жизнь объекта класса. В данном случае использование boost::threads позволяет отказаться от использования статических функций в классе для запуска потока. > > Именно так. Более того, совершенно нежелательно ассоциировать экземпляр класса с потоком, т.к. потоков у одного экземпляра может быть и несколько, и они выполняют совершенно разные задачи.
Не вижу связи, между частями сложного предложения. Понятия класс/экземпляр и поток совершенно ортогональны. Класс, данном контексте, — способ организации исходного кода. Поток — сущность, которая исполняет код.
Экземпляр класса может создать хоть M, или даже N потоков и направить их куда ему нужно.
> Судя по всему, реализовать это при помощи boost::threads очень даже можно, но так уж получается, что я предпочитаю не использовать высокоуровневые обертки там, где без этого можно обойтись. И бью других по рукам за излишнее усложнение кода там, где можно обойтись более простыми и архитектурно чистыми решениями.
Это вы, батенька, очень напрасно. boost::threads элегантно и эффективно решает рутинную задачу создания потоков. boost::threads документирован и его назначение недвусмысленно. ИМХО, намного проще читать и понимать код, который оперирует стандартными высокоуровневыми сущностями, чем код, который реализует те же задачи посредством низкоуровневых API вызовов.
Кроме того, использование сущностей высокого уровня позволяет инженеру сосредоточиться на problem domain, а не на том, например, в каком параметре передать указатель на ф-цию в вызове CreateThread() и не забыть проверить и правильно интерпретировать возвращаемое значение.
ME>Не вижу связи, между частями сложного предложения. Понятия класс/экземпляр и поток совершенно ортогональны. Класс, данном контексте, — способ организации исходного кода. Поток — сущность, которая исполняет код.
А кто сказал что они связаны? Это как раз в стиле VCL привязывать класс к потоку. Я этого избегаю.
>> Судя по всему, реализовать это при помощи boost::threads очень даже можно, но так уж получается, что я предпочитаю не использовать высокоуровневые обертки там, где без этого можно обойтись. И бью других по рукам за излишнее усложнение кода там, где можно обойтись более простыми и архитектурно чистыми решениями.
ME>Это вы, батенька, очень напрасно. boost::threads элегантно и эффективно решает рутинную задачу создания потоков. boost::threads документирован и его назначение недвусмысленно. ИМХО, намного проще читать и понимать код, который оперирует стандартными высокоуровневыми сущностями, чем код, который реализует те же задачи посредством низкоуровневых API вызовов.
ME>Кроме того, использование сущностей высокого уровня позволяет инженеру сосредоточиться на problem domain, а не на том, например, в каком параметре передать указатель на ф-цию в вызове CreateThread() и не забыть проверить и правильно интерпретировать возвращаемое значение.
На проблеме позволяет сосредоточится наличие различных звеньев на этапе программинга — девелопер (coder), pj mgr и DoD. Все остальное ничего не меняет радикально и является ересью в высшем проявлении оной.
PS Я задал совершенно конкретный вопрос и получил на него совершенно конкретный ответ от Павла, за что ему большое спасибо. Вам тоже большое спасибо за то, что просвятили про boost:threads, но когда я спрашиваю "мне нужно белое", а мне говорят "вот тебе зеленое, оно лучше", это не очень приятно, поверьте, т.к. уводит собственно от темы вопроса. Тем более что мне нужно белое, а не зеленое, и я отнюдь не дальтоник. Работа в команде накладывает некоторые ограничения на используемую палитру
Всем спасибо за содействие!
Здравствуйте, Xor., Вы писали:
X>Я сталкиваюсь часто со следующими проблемами при написании multi threaded applications:
X>1) Thread safe methods call X>2) Stop/Start threads synchronization X>3) Releasing the associated data
X>И не испытываю проблем по запуску функции в отдельном потоке и созданию CS and Mutecies.
X>Вот и вопрос, какие решения предлагает boost?
X>Хочу сразу отметить, что наличие в нём элементов позволяющих создать решение не означает, что это и есть решение.
Кстати говоря, типичная задача системы массового обслуживания — ожидание набора событий ("условий", в терминах boost).
В WinAPI это сделано, на мой взгляд, наиболее грамотно.
У меня не много опыта программирования под другими ОС, но такое впечатление, что человеческая фантазия упёрлась в POSIX, где этого не было предусмотрено.
Так, например, в vxWorks дизъюнктивное ожидание можно устроить лишь для именованых каналов. В результате, пришлось реализовать логику событий, мутексов и потоков-как-синхрообъектов на каналах. Веселуха ещё та, хотя и работает, правда, в разы медленнее, чем могло бы.
Вы скажете, нафига дизъюнктивное ожидание мутекса/семафора, а тем более — потока? Да очень даже нафига.
Если поток-хозяин хочет сообщить потоку-слуге, что пора бы завершиться — он взводит событие (хотя бы флажок) и надеется на скорую кончину слуги.
В это время слуга пытается чего-то дождаться. Вот он сперва дождётся, а потом проверит флажок, и, устыдившись, отвалит.
А в это же самое время поток-хозяин нервно курит. Он уже послал чёрную метку слуге и ждёт его кончины. Сколько он прокукует — неизвестно.
Это совершенно не увязывается с реалтаймом, где нужно гарантированное время отклика (пусть даже и не маленькое).
Выходов всего три.
Или делать нормальный API работы с синхрообъектами, умеющий ждать дизъюнктивно (конъюнктивно-то просто: выполнил подряд все ожидания — и готово).
Или мониторить состояние синхрообъектов в цикле
while(true)
{
if(clock() > timeout) return objects_end;
for(objects_iterator it = objects_begin; it != objects_end; ++it)
if(ready(*it)) return it;
sleep(epsilon); // позволить другим потокам тоже что-то поделать в этом кванте
// величина epsilon находится из компромисса между нужным временем отклика и загрузкой процессора
}
Или завести общий синхрообъект, и в цикле ожидать именно его.
... << RSDN@Home 1.1.0 stable >>
Перекуём баги на фичи!
Re[7]: member function as template argument (VC6) workaround
К>Выходов всего три. К>Или делать нормальный API работы с синхрообъектами, умеющий ждать дизъюнктивно (конъюнктивно-то просто: выполнил подряд все ожидания — и готово). К>Или мониторить состояние синхрообъектов в цикле К>
К>while(true)
К>{
К> if(clock() > timeout) return objects_end;
К> for(objects_iterator it = objects_begin; it != objects_end; ++it)
К> if(ready(*it)) return it;
К> sleep(epsilon); // позволить другим потокам тоже что-то поделать в этом кванте
К> // величина epsilon находится из компромисса между нужным временем отклика и загрузкой процессора
К>}
К>
К>Или завести общий синхрообъект, и в цикле ожидать именно его.
Общий, но не для всех :)
Объект, принимающий сообщения для потока:
thread::join_all();
}
---------------------------------------
Но! Если в одном из алгоритмов dispatch нужно сделать ожидание:
bool dispatch( Event& e ){
...
wait_for( some_other_event );
...
}
Очевидно, что здесь ждут только одного события. В случае же когда нужно обработать выход, то видимо это должно быть исключение функции ожидания, поскольку явно алгоритм не предусматривает обработку других событий кроме ожидаемого.
Xor. wrote:
> Здравствуйте, MaximE, > > Я сталкиваюсь часто со следующими проблемами при написании multi threaded applications: > > 1) Thread safe methods call > 2) Stop/Start threads synchronization > 3) Releasing the associated data
Не совсем понял про 2, и совсем не понял про 3.
> И не испытываю проблем по запуску функции в отдельном потоке и созданию CS and Mutecies. > > Вот и вопрос, какие решения предлагает boost?
Буст предлагает условно мультиплатформенные потоки и примитивы синхронизации. Все решения буста задокументированы.
> Хочу сразу отметить, что наличие в нём элементов позволяющих создать решение не означает, что это и есть решение.