Re: [ANN] The Future of Concurrency in C++
От: Аноним  
Дата: 05.05.08 22:24
Оценка:
R>Так же описывается, что нас ждёт в более отдалённом будущем (в boost, будем надеятся значительно раньше):
R> — Пулы потоков (thread pools)
А смысл их отделять от обычных потоков? Если уж ввели некую абстракцию самого потока, то почему бы в имплементации не делать пул потоков или юзать механизм предоставляемый ОС (намекаю на мой любимый QueueUserWorkItem)?

R> — Транзакционная память и автоматическое распараллеливание (software transactional memory, auto-parallelisation)

Второе умеет уже сейчас Intel CPP
Re[10]: [ANN] The Future of Concurrency in C++
От: dip_2000 Россия  
Дата: 06.05.08 04:02
Оценка: +1
Здравствуйте, Roman Odaisky, Вы писали:

RO>(Главное) Выставляет наружу объекты синхронизации.

RO>Сигналит даже тогда, когда никто не слушает.
RO>Требует WaitForMultipleObjects или велосипед.
RO>Поди разбери, что именно сработало (недостаток именно WFMO, а не подхода).
RO>Строгие ограничения на количество объектов (недостаток именно WFMO, а не подхода).
RO>Относительный таймаут (недостаток именно WFMO, а не подхода).

RO>Я поискал немного по RSDN, все увиденные мной упоминания WFMO, кроме одного, были связаны с ожиданием завершения N-ного числа потоков. Хотя это то же самое, что дожидаться завершения по одному.


(краткий ответ на этот и предыдущий пост, ибо согласен что это флейм )
Вся прелесть(и, одновременно идеологическое зло) Что можно разом дожидаться разнородных сигналов(там выше описано каких). Позволяет существенно упростить... я бы сказал логику, и при условии что вы хорошо представляете механизмы синхронизации WinAPI это будет верно, для всех остальных случаев подойдет фраза "снизить количество строк"(хотя убить того программиста, у которого это единственная цель )

RO>Приведи пример кода, который использует WaitForMultipleObjects и без него никак не обойдется.


Разумееется такого нет Всегда можно сильно или не сильно извратиться и написать код, делающий то же самое. Но после понимания WFMO(или прожигания мозга антинаучным API, для кого как), и его использования, код становится проще, лаконечнее. Можно сказать удобнее в написании для пользователя API.

PS Я абсолютно согласен что идеологически, изначально подход был не оптимальным, и, возможно не лучшим(можно еще порассуждать про "в UNIX все файлы"). НО. То что получилось, при адекватном использовании, крайне удобно.
Re[2]: [ANN] The Future of Concurrency in C++
От: remark Россия http://www.1024cores.net/
Дата: 06.05.08 06:33
Оценка:
Здравствуйте, Аноним, Вы писали:

R>>Так же описывается, что нас ждёт в более отдалённом будущем (в boost, будем надеятся значительно раньше):

R>> — Пулы потоков (thread pools)

А>А смысл их отделять от обычных потоков? Если уж ввели некую абстракцию самого потока, то почему бы в имплементации не делать пул потоков


Об этом и идёт речь. Просто одно дело, когда ты у себя в программе быстренько делаешь пул потоков на скорую руку. И другое дело, описать это формально на уровне стандарта ISO, какие у него возможности, какие к нему требования, какие он даёт гарантии и т.д. В данный момент у комитета стандартизации есть более важные вопросы — ещё модель памяти до конца не утвердили.


А>или юзать механизм предоставляемый ОС (намекаю на мой любимый QueueUserWorkItem)?


Ну типа они предполагают, что С++ будет работать ещё на паре ОС, кроме Windows.


R>> — Транзакционная память и автоматическое распараллеливание (software transactional memory, auto-parallelisation)


А>Второе умеет уже сейчас Intel CPP


У Intel есть компилятор, который и первое умеет. Только пока, по-моему, от этого никому ни жарко, ни холодно.



1024cores — all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[11]: [ANN] The Future of Concurrency in C++
От: Roman Odaisky Украина  
Дата: 06.05.08 09:24
Оценка: 1 (1)
Здравствуйте, Left2, Вы писали:

RO>>Речь о (проблемном) подходе, в котором события, на которых можно блокироваться, выставляются как часть интерфейса.

L>Всё равно не пойму. При чём тут сам WFMO тогда? И что плохого в том что события выставляются как часть интерфейса? Чем это принципиально отличается от "дёрни меня за эту функцию в случае 1, и вот за эту в случае 2"?

Во-первых, это сильно вредит портируемости.

Во-вторых, это решение задачи не с той стороны. К примеру, некий поток собирает данные и выставляет событие всякий раз, как доступна новая порция данных. Если ты хочешь обрабатывать эти данные, тебе непременно нужен другой поток, который будет блокироваться на том событии. Что же делать, если у тебя есть, к примеру, атомарная функция, в которую эти данные нужно передавать, не пытаясь кого-нибудь разбудить?

Вот примерно так это выглядит с коллбеками:
class Gatherer
{
public:
    void operator()()
    {
        while(boost::optional<Data> data = readData())
        {
            boost::unique_lock<boost::mutex> lock(m_mutexSignal);
            m_signal(*data);
        }
    }

    boost::signals::connection addCallback(boost::function<void (Data const &)> callback)
    {
        boost::unique_lock<boost::mutex> lock(m_mutexSignal);
        return m_signal.connect(callback);
    }

private:
    boost::mutex m_mutexSignal;
    boost::signal<void (Data const &)> m_signal;
};

class Processor
{
public:
    Processor(): m_shouldStop(false)
    {
    }

    void operator()()
    {
        boost::unique_lock<boost::mutex> lock(m_mutex);

        while(1)
        {
            if(m_shouldStop)
            {
                return;
            }
            if(m_data)
            {
                doSomething(*m_data);
                m_data = boost::none;
            }
            m_ping.wait(lock);
        }
    }

    void incoming(Data const& data)
    {
        boost::unique_lock<boost::mutex> lock(m_mutex);
        m_data = data;
        m_ping.notify_all();
    }

    void stop()
    {
        boost::unique_lock<boost::mutex> lock(m_mutex);
        m_shouldStop = true;
        m_ping.notify_all();
    }

private:
    bool m_shouldStop;
    boost::optional<Data> m_data;

    boost::mutex m_mutex;
    boost::condition_variable_any m_ping;
};

Всё красиво и аккуратно. Синхронизация только там, где нужна. Может быть сколько угодно экземпляров обоих классов. Для демонстрации ожидания двух событий я сделал флаг остановки, хотя на самом деле он не требуется — можно вызвать boost::thread::interrupt() и из ближайшей блокирующей функции вылетит исключение. Т. е., обработчик можно было переписать попросту так:
boost::unique_lock<boost::mutex> lock(m_mutex);
while(1)
{
    m_ping.wait(lock, boost::bind(&boost::optional<Data>::is_initialized, m_data); // или boost::lambda::var(m_data)
    doSomething(*m_data);
    m_data = boost::none;
}

Если же собиратель данных будет выставлять наружу событие, то он должен будет, во-первых, держать у себя backlog данных, которые у него еще не забрали, во-вторых, будет обязательно будить все потоки (в то время как коллбек может принять решение, что эта порция данных ему неинтересна), в-третьих, я не уверен, но выглядит похоже на priority inversion в особо несчастных случаях.

Тем более, что в ядре всё равно где-то находится список тех, кто ожидает наступления данного события, — т. е., набор «коллбеков» всё равно есть, только спрятан! Лучше уж написать его самому так, как надо.
До последнего не верил в пирамиду Лебедева.
Re[12]: [ANN] The Future of Concurrency in C++
От: dip_2000 Россия  
Дата: 06.05.08 10:13
Оценка:
Здравствуйте, Roman Odaisky, Вы писали:

Извиняюсь, что опять вклиниваюсь в дискуссию

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],); // вот тут дождались


Re[13]: извиняюсь за "стиль"
От: dip_2000 Россия  
Дата: 06.05.08 10:15
Оценка:
Здравствуйте, dip_2000, Вы писали:

писалось для краткой иллюстрации идеи
Re[13]: [ANN] The Future of Concurrency in C++
От: Roman Odaisky Украина  
Дата: 06.05.08 10:53
Оценка:
Здравствуйте, 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 системных вызовов (если потоки успеют сделать свое черное дело).
До последнего не верил в пирамиду Лебедева.
Re[14]: [ANN] The Future of Concurrency in C++
От: dip_2000 Россия  
Дата: 06.05.08 13:04
Оценка:
Здравствуйте, Roman Odaisky, Вы писали:

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


RO>>>Во-первых, это сильно вредит портируемости.

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

RO>Поинтересуйся как-нибудь, на скольки платформах доступны pthreads и GNU Pth. (Подсказка: «p» — это POSIX.)

Какое уважение к собеседнику Спасибо, поинтересуюсь обязательно.
RO>Я же специально упомянул этот случай выше по ветке.
RO>С очень простой стороны:
RO>
RO>for(std::thread& t : pool) // C++09
RO>{
RO>    t.join();
RO>}
RO>


RO>Здесь вполне можно дождаться завершения первого, затем второго и т. д. Тем более, что с высокой вероятностью здесь будет 0—1 системных вызовов (если потоки успеют сделать свое черное дело).

Это уже не язык для перфоманса, имхо.... Вобщем я получил ответ
Re[5]: [ANN] The Future of Concurrency in C++
От: Roman Odaisky Украина  
Дата: 06.05.08 13:12
Оценка:
Здравствуйте, jazzer, Вы писали:

J>как жалуется Строуструп, если бы не бюрократия, которая царит в ISO, стандарты бы принимались гораздо быстрее.

J>По его оценкам, стандарт будет готов осенью 2008-го года (финальная версия), а потом около года проваландается по инстанциям до официального издания.
J>Т.е. к моменту официального выхода комитет уже год как будет работать над TR2.

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

Интересно, сможет ли комитет в течение того года фиксить баги, которые обнаружат многочисленные «бета-тестеры», или будет складывать их в долгий ящик^W^W^W в TR2?
До последнего не верил в пирамиду Лебедева.
Re[15]: [ANN] The Future of Concurrency in C++
От: Roman Odaisky Украина  
Дата: 06.05.08 14:26
Оценка:
Здравствуйте, dip_2000, Вы писали:

RO>>С очень простой стороны:

RO>>
RO>>for(std::thread& t : pool) // C++09
RO>>{
RO>>    t.join();
RO>>}
RO>>


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);

// вариант 2
for(std::size_t i = 0; i < N; ++i)
{
    WaitForSingleObject(pool[i], INFINITE);
}

Что выполнится быстрее? Я уверен, что разницы во времени исполнения нет.
До последнего не верил в пирамиду Лебедева.
Re[16]: [ANN] The Future of Concurrency in C++
От: dip_2000 Россия  
Дата: 06.05.08 15:17
Оценка:
Здравствуйте, Roman Odaisky, Вы писали:


RO>Что выполнится быстрее? Я уверен, что разницы во времени исполнения нет.

я уверен что разницы между
++i и i++ в 98% случаев нет, и тем не менее в приличном обществе так не пишут. Думаю по понятным причинам. Код пишется не на 1 частный случай. Этот же код может работать, работать, а потом выстрелить в совершенно других условиях.
Вобщем продолжать смысла нет. Думаю все все поняли
Re[17]: [ANN] The Future of Concurrency in C++
От: Roman Odaisky Украина  
Дата: 06.05.08 15:51
Оценка:
Здравствуйте, dip_2000, Вы писали:

_>я уверен что разницы между

_>++i и i++ в 98% случаев нет, и тем не менее в приличном обществе так не пишут. Думаю по понятным причинам. Код пишется не на 1 частный случай. Этот же код может работать, работать, а потом выстрелить в совершенно других условиях.
_>Вобщем продолжать смысла нет. Думаю все все поняли :-)

Так вот... К чему это я?

В последнем черновике стандарта предусмотрен метод std::thread_group::join_all().

В Boost 1.35 на всех платформах он реализован как join в цикле.

Так что в ногу здесь не выстрелишь.
До последнего не верил в пирамиду Лебедева.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.