R>Так же описывается, что нас ждёт в более отдалённом будущем (в boost, будем надеятся значительно раньше): R> — Пулы потоков (thread pools)
А смысл их отделять от обычных потоков? Если уж ввели некую абстракцию самого потока, то почему бы в имплементации не делать пул потоков или юзать механизм предоставляемый ОС (намекаю на мой любимый QueueUserWorkItem)?
R> — Транзакционная память и автоматическое распараллеливание (software transactional memory, auto-parallelisation)
Второе умеет уже сейчас Intel CPP
Здравствуйте, Roman Odaisky, Вы писали:
RO>(Главное) Выставляет наружу объекты синхронизации. RO>Сигналит даже тогда, когда никто не слушает. RO>Требует WaitForMultipleObjects или велосипед. RO>Поди разбери, что именно сработало (недостаток именно WFMO, а не подхода). RO>Строгие ограничения на количество объектов (недостаток именно WFMO, а не подхода). RO>Относительный таймаут (недостаток именно WFMO, а не подхода).
RO>Я поискал немного по RSDN, все увиденные мной упоминания WFMO, кроме одного, были связаны с ожиданием завершения N-ного числа потоков. Хотя это то же самое, что дожидаться завершения по одному.
(краткий ответ на этот и предыдущий пост, ибо согласен что это флейм )
Вся прелесть(и, одновременно идеологическое зло) Что можно разом дожидаться разнородных сигналов(там выше описано каких). Позволяет существенно упростить... я бы сказал логику, и при условии что вы хорошо представляете механизмы синхронизации WinAPI это будет верно, для всех остальных случаев подойдет фраза "снизить количество строк"(хотя убить того программиста, у которого это единственная цель )
RO>Приведи пример кода, который использует WaitForMultipleObjects и без него никак не обойдется.
Разумееется такого нет Всегда можно сильно или не сильно извратиться и написать код, делающий то же самое. Но после понимания WFMO(или прожигания мозга антинаучным API, для кого как), и его использования, код становится проще, лаконечнее. Можно сказать удобнее в написании для пользователя API.
PS Я абсолютно согласен что идеологически, изначально подход был не оптимальным, и, возможно не лучшим(можно еще порассуждать про "в UNIX все файлы"). НО. То что получилось, при адекватном использовании, крайне удобно.
Здравствуйте, Аноним, Вы писали:
R>>Так же описывается, что нас ждёт в более отдалённом будущем (в boost, будем надеятся значительно раньше): R>> — Пулы потоков (thread pools)
А>А смысл их отделять от обычных потоков? Если уж ввели некую абстракцию самого потока, то почему бы в имплементации не делать пул потоков
Об этом и идёт речь. Просто одно дело, когда ты у себя в программе быстренько делаешь пул потоков на скорую руку. И другое дело, описать это формально на уровне стандарта ISO, какие у него возможности, какие к нему требования, какие он даёт гарантии и т.д. В данный момент у комитета стандартизации есть более важные вопросы — ещё модель памяти до конца не утвердили.
А>или юзать механизм предоставляемый ОС (намекаю на мой любимый QueueUserWorkItem)?
Ну типа они предполагают, что С++ будет работать ещё на паре ОС, кроме Windows.
R>> — Транзакционная память и автоматическое распараллеливание (software transactional memory, auto-parallelisation)
А>Второе умеет уже сейчас Intel CPP
У Intel есть компилятор, который и первое умеет. Только пока, по-моему, от этого никому ни жарко, ни холодно.
Здравствуйте, Left2, Вы писали:
RO>>Речь о (проблемном) подходе, в котором события, на которых можно блокироваться, выставляются как часть интерфейса. L>Всё равно не пойму. При чём тут сам WFMO тогда? И что плохого в том что события выставляются как часть интерфейса? Чем это принципиально отличается от "дёрни меня за эту функцию в случае 1, и вот за эту в случае 2"?
Во-первых, это сильно вредит портируемости.
Во-вторых, это решение задачи не с той стороны. К примеру, некий поток собирает данные и выставляет событие всякий раз, как доступна новая порция данных. Если ты хочешь обрабатывать эти данные, тебе непременно нужен другой поток, который будет блокироваться на том событии. Что же делать, если у тебя есть, к примеру, атомарная функция, в которую эти данные нужно передавать, не пытаясь кого-нибудь разбудить?
Всё красиво и аккуратно. Синхронизация только там, где нужна. Может быть сколько угодно экземпляров обоих классов. Для демонстрации ожидания двух событий я сделал флаг остановки, хотя на самом деле он не требуется — можно вызвать boost::thread::interrupt() и из ближайшей блокирующей функции вылетит исключение. Т. е., обработчик можно было переписать попросту так:
Если же собиратель данных будет выставлять наружу событие, то он должен будет, во-первых, держать у себя backlog данных, которые у него еще не забрали, во-вторых, будет обязательно будить все потоки (в то время как коллбек может принять решение, что эта порция данных ему неинтересна), в-третьих, я не уверен, но выглядит похоже на priority inversion в особо несчастных случаях.
Тем более, что в ядре всё равно где-то находится список тех, кто ожидает наступления данного события, — т. е., набор «коллбеков» всё равно есть, только спрятан! Лучше уж написать его самому так, как надо.
Извиняюсь, что опять вклиниваюсь в дискуссию
RO>Во-первых, это сильно вредит портируемости.
Есть в практике слычаи, когда не только с Винды код переезжает, а и НА нее другие системы не очень заботятся о портируемости многопоточного кода, почему здесь это приводится как минус ?
RO>Во-вторых, это решение задачи не с той стороны.
А вот скажите, с какой стороны надо решать задачу подобного плана :
есть массив структур. для каждой создается массив обработчик. функция потока получает указатель на "свою" структуру. Отрабатывает, изменяет структуру, и поток умирает. Вопрос : зачем тут плодить множество сущностей ? зачем потоку в конце жизни выставлять событие (сигнал, дергать коллбэк)?
//на глазокstruct some_mega_struct{ int i_; };
int ThreadFoo(void* pContext)
{
some_mega_struct* p = static_cast<some_mega_struct*>(pContext);
(pContext->i)++;
return 0;
}
HANDLE hVec[16];
some_mega_struct vec_some_mega_struct[16];
for (size_t i = 0; i != 16; ++i )
hVec[i] = CreateThread(...,&vec_some_mega_struct[i]);
WFMO(sizeof(hVec) ,&hVec[0],); // вот тут дождались
Здравствуйте, dip_2000, Вы писали:
RO>>Во-первых, это сильно вредит портируемости. _>Есть в практике слычаи, когда не только с Винды код переезжает, а и НА нее :) другие системы не очень заботятся о портируемости многопоточного кода, почему здесь это приводится как минус ? :xz:
Поинтересуйся как-нибудь, на скольки платформах доступны pthreads и GNU Pth. (Подсказка: «p» — это POSIX.)
RO>>Во-вторых, это решение задачи не с той стороны. _>А вот скажите, с какой стороны надо решать задачу подобного плана :
_>
_>for (size_t i = 0; i != 16; ++i )
_>hVec[i] = CreateThread(...,&vec_some_mega_struct[i]);
_>WFMO(sizeof(hVec) ,&hVec[0],); // вот тут дождались
_>
Я же специально упомянул этот случай выше по ветке.
С очень простой стороны:
for(std::thread& t : pool) // C++09
{
t.join();
}
Здесь вполне можно дождаться завершения первого, затем второго и т. д. Тем более, что с высокой вероятностью здесь будет 0—1 системных вызовов (если потоки успеют сделать свое черное дело).
Здравствуйте, Roman Odaisky, Вы писали:
RO>Здравствуйте, dip_2000, Вы писали:
RO>>>Во-первых, это сильно вредит портируемости. _>>Есть в практике слычаи, когда не только с Винды код переезжает, а и НА нее другие системы не очень заботятся о портируемости многопоточного кода, почему здесь это приводится как минус ?
RO>Поинтересуйся как-нибудь, на скольки платформах доступны pthreads и GNU Pth. (Подсказка: «p» — это POSIX.)
Какое уважение к собеседнику Спасибо, поинтересуюсь обязательно. RO>Я же специально упомянул этот случай выше по ветке. RO>С очень простой стороны: RO>
RO>Здесь вполне можно дождаться завершения первого, затем второго и т. д. Тем более, что с высокой вероятностью здесь будет 0—1 системных вызовов (если потоки успеют сделать свое черное дело).
Это уже не язык для перфоманса, имхо.... Вобщем я получил ответ
Здравствуйте, jazzer, Вы писали:
J>как жалуется Строуструп, если бы не бюрократия, которая царит в ISO, стандарты бы принимались гораздо быстрее. J>По его оценкам, стандарт будет готов осенью 2008-го года (финальная версия), а потом около года проваландается по инстанциям до официального издания. J>Т.е. к моменту официального выхода комитет уже год как будет работать над TR2.
Наверное, это даже к лучшему. Это даст время производителям компиляторов. Они смогут спокойно реализовать все фичи, выпустить пару бет, исправить баги, и торжественно выпустить новую версию вскоре после официального выхода.
Интересно, сможет ли комитет в течение того года фиксить баги, которые обнаружат многочисленные «бета-тестеры», или будет складывать их в долгий ящик^W^W^W в TR2?
RO>>Здесь вполне можно дождаться завершения первого, затем второго и т. д. Тем более, что с высокой вероятностью здесь будет 0—1 системных вызовов (если потоки успеют сделать свое черное дело). _>Это уже не язык для перфоманса, имхо.... Вобщем я получил ответ :beer:
Завершение потока — это частный случай. Хотя бы потому, что поток по определению не может сообщить наружу, что он завершился :-)
Проведи как-нибудь эксперимент:
void f(void *)
{
Sleep(100);
}
std::vector<HANDLE> pool(N);
for(std::size_t i = 0; i < N; ++i)
{
pool[i] = (HANDLE)_beginthreadex(0, 0, f, 0, 0, 0);
}
// вариант 1
WaitForMultipleObjects(N, &pool.begin(), TRUE, INFINITE);
// вариант 2for(std::size_t i = 0; i < N; ++i)
{
WaitForSingleObject(pool[i], INFINITE);
}
Что выполнится быстрее? Я уверен, что разницы во времени исполнения нет.
RO>Что выполнится быстрее? Я уверен, что разницы во времени исполнения нет.
я уверен что разницы между
++i и i++ в 98% случаев нет, и тем не менее в приличном обществе так не пишут. Думаю по понятным причинам. Код пишется не на 1 частный случай. Этот же код может работать, работать, а потом выстрелить в совершенно других условиях.
Вобщем продолжать смысла нет. Думаю все все поняли
Здравствуйте, dip_2000, Вы писали:
_>я уверен что разницы между _>++i и i++ в 98% случаев нет, и тем не менее в приличном обществе так не пишут. Думаю по понятным причинам. Код пишется не на 1 частный случай. Этот же код может работать, работать, а потом выстрелить в совершенно других условиях. _>Вобщем продолжать смысла нет. Думаю все все поняли :-)
Так вот... К чему это я?
В последнем черновике стандарта предусмотрен метод std::thread_group::join_all().
В Boost 1.35 на всех платформах он реализован как join в цикле.