синхронизация потоков
От: tdiff  
Дата: 28.03.14 14:21
Оценка: 1 (1) :))) :))
Возьмём классический пример синхронизации потоков:


bool flag = false;
void thread_1()
{
    while (!flag) {
        work();
    }
}

void thread_2()
{
    sleep(1000);
    flag = true;
}

В функции work() flag не меняется.

Подобный код приводится в качестве примера ошибочного, т.к. может быть оптимизирован компилятором до

void thread_1_real()
{
    bool f = flag;
    while (!f) {
        work();
    }
}

Такая оптимизация возможна, т.к. компилятор видит, что раз внутри work() flag не меняется, поэтому
нет смысла считывать его на каждой итерации.


Стандартное в практике решение этой проблемы — использовать mutex:
mutex m;
 bool flag = false;

void thread_1_sync()
{
    while (true) {
        {
            scoped_lock lock(m);
            if (flag) break;
        }
        work();
    }
}

void thread_2_sync()
{
    sleep(1000);
    {
        scoped_lock lock(m);
        flag = true;
    }
}


Вопрос: что помешает компилятору и в этом случае оптимизировать чтение значения flag примерно вот так:
void thread_1_sync_real()
{
    bool f;
    scoped_lock lock(m) {
        f = flag;
    }
    while (true) {
        if (f) break;
        work();
    }
}


Моё предположение такое: где-то внутри mutex используется какая-то специальная инструкция типа memory barrier, которую компилятор не может позволить себе отоптимизировать.
Так ли это? Если да, то что это за инструкция на самом деле?
Re: синхронизация потоков
От: fdn721  
Дата: 28.03.14 14:42
Оценка: 1 (1) +2 -1
Здравствуйте, tdiff, Вы писали:

T>Подобный код приводится в качестве примера ошибочного, т.к. может быть оптимизирован компилятором до


Добавь volatile, и всё будет работать корректно.


volatile bool m_flag;
Re[2]: синхронизация потоков
От: Kernan Ниоткуда https://rsdn.ru/forum/flame.politics/
Дата: 28.03.14 14:44
Оценка:
Здравствуйте, fdn721, Вы писали:

F>Добавь volatile, и всё будет работать корректно.

Не совсем чтобы корректно, но будет работать, да.
Sic luceat lux!
Re: синхронизация потоков
От: Lazin Россия http://evgeny-lazin.blogspot.com
Дата: 28.03.14 14:56
Оценка: +1
Здравствуйте, tdiff, Вы писали:

T>Моё предположение такое: где-то внутри mutex используется какая-то специальная инструкция типа memory barrier, которую компилятор не может позволить себе отоптимизировать.

T>Так ли это? Если да, то что это за инструкция на самом деле?

как правило, генерируется полный барьер, например в boost/shared_ptr/detail/spin_lock.hpp для этого используется макрос

#define BOOST_COMPILER_FENCE __asm__ __volatile__( "" : : : "memory" );


должен ли компилятор в данном случае оптимизировать именение переменной несмотря на барьер я не знаю

сам mutex может быть устроен по разному, это может быть простой спин-лок на атомарных операциях (InterlockedExchange), может быть примитив операционной системы, который идет в ядро, а может быть что-то более сложно, на основе очередей
Re[2]: синхронизация потоков
От: tdiff  
Дата: 28.03.14 14:57
Оценка:
Здравствуйте, fdn721, Вы писали:

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


T>>Подобный код приводится в качестве примера ошибочного, т.к. может быть оптимизирован компилятором до


F>Добавь volatile, и всё будет работать корректно.



F>
F>volatile bool m_flag;
F>



volatile это стрёмно.
Если не ошибаюсь, в стандарте ++ его вообще нет, так что каждый компилятор ведёт себя, как захочется.
Re[2]: синхронизация потоков
От: Lazin Россия http://evgeny-lazin.blogspot.com
Дата: 28.03.14 15:01
Оценка: 1 (1)
Здравствуйте, Lazin, Вы писали:

L>должен ли компилятор в данном случае оптимизировать именение переменной несмотря на барьер я не знаю


думаю без volatile в данном случае все же нельзя, даже если компилятор учитывает наличие барьера (сомневаюсь в этом) и не будет оптимизировать и выносить проверку переменной из цикла, он все равно может поместить ее в регистр, что, опять же, изменит ее видимость для другого потока
Re: синхронизация потоков
От: dead0k  
Дата: 28.03.14 15:10
Оценка: -1
Здравствуйте, tdiff, Вы писали:

T>
T>void thread_1_sync()
T>{
T>    while (true) {
T>        {
T>            scoped_lock lock(m);
T>            if (flag) break;
T>        }
T>        work();
T>    }
T>}


flag — глобальная переменная, которую библиотечная функция lock(...) — видит и, как следствие, может изменить, что приведет к завершению(или продолжению) цикла.
если компилятор перестроит тело так, как во втором примере, видимое поведение функции thrad_1_sync изменится. следовательно компилятор так делать не может.
замечательный факт — про многопоточность мы еще не говорили
Re[2]: синхронизация потоков
От: Lazin Россия http://evgeny-lazin.blogspot.com
Дата: 28.03.14 15:13
Оценка:
Здравствуйте, dead0k, Вы писали:

D>flag — глобальная переменная, которую библиотечная функция lock(...) — видит и, как следствие, может изменить, что приведет к завершению(или продолжению) цикла.

D>если компилятор перестроит тело так, как во втором примере, видимое поведение функции thrad_1_sync изменится. следовательно компилятор так делать не может.
D>замечательный факт — про многопоточность мы еще не говорили

А если ф-я lock в той же еденице трансляции? Очень актуально для header only реализаций, как в boost/shared_ptr.
Re[3]: синхронизация потоков
От: tdiff  
Дата: 28.03.14 15:15
Оценка:
Здравствуйте, Lazin, Вы писали:

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


L>>должен ли компилятор в данном случае оптимизировать именение переменной несмотря на барьер я не знаю


L>думаю без volatile в данном случае все же нельзя, даже если компилятор учитывает наличие барьера (сомневаюсь в этом) и не будет оптимизировать и выносить проверку переменной из цикла, он все равно может поместить ее в регистр, что, опять же, изменит ее видимость для другого потока


ну вот тут пишут, что локов достаточно:
https://code.google.com/p/thread-sanitizer/wiki/PopularDataRaces#Notification :
To fix such race you need to use compiler and/or memory barriers (if you are not an expert -- simply use locks).

Вроде это достаточно серьёзный проект: часть clang и gcc.
Кстати, возможно, из этого следует, что слова насчёт locks применимы именно к этим компиляторам.
Re[3]: синхронизация потоков
От: dead0k  
Дата: 28.03.14 15:18
Оценка:
Здравствуйте, Lazin, Вы писали:

L>А если ф-я lock в той же еденице трансляции? Очень актуально для header only реализаций, как в boost/shared_ptr.

А разве есть header-only библиотеки синхронизации?
тот-же буст пользует pthread/winapi
Re[2]: синхронизация потоков
От: tdiff  
Дата: 28.03.14 15:19
Оценка:
Здравствуйте, dead0k, Вы писали:

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


T>>
T>>void thread_1_sync()
T>>{
T>>    while (true) {
T>>        {
T>>            scoped_lock lock(m);
T>>            if (flag) break;
T>>        }
T>>        work();
T>>    }
T>>}


D>flag — глобальная переменная, которую библиотечная функция lock(...) — видит и, как следствие, может изменить, что приведет к завершению(или продолжению) цикла.

D>если компилятор перестроит тело так, как во втором примере, видимое поведение функции thrad_1_sync изменится. следовательно компилятор так делать не может.
D>замечательный факт — про многопоточность мы еще не говорили

А work() тоже видит flag. Следовательно, по-вашему, оптимизация thread_1_real() невозможна или я что-то недопонял?
Re[3]: синхронизация потоков
От: tdiff  
Дата: 28.03.14 15:22
Оценка:
Здравствуйте, tdiff, Вы писали:

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


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


T>>>
T>>>void thread_1_sync()
T>>>{
T>>>    while (true) {
T>>>        {
T>>>            scoped_lock lock(m);
T>>>            if (flag) break;
T>>>        }
T>>>        work();
T>>>    }
T>>>}


D>>flag — глобальная переменная, которую библиотечная функция lock(...) — видит и, как следствие, может изменить, что приведет к завершению(или продолжению) цикла.

D>>если компилятор перестроит тело так, как во втором примере, видимое поведение функции thrad_1_sync изменится. следовательно компилятор так делать не может.
D>>замечательный факт — про многопоточность мы еще не говорили

T>А work() тоже видит flag. Следовательно, по-вашему, оптимизация thread_1_real() невозможна или я что-то недопонял?


кажется понял — дело в том, что она библиотечная.
Re[3]: синхронизация потоков
От: dead0k  
Дата: 28.03.14 15:25
Оценка:
Здравствуйте, tdiff, Вы писали:
T>А work() тоже видит flag. Следовательно, по-вашему, оптимизация thread_1_real() невозможна или я что-то недопонял?
если work — находится в другой единице трансляции, то да (тут правда может еще сыграть whole program optimization, в энтом деле я не соображаю)
Re[4]: синхронизация потоков
От: Lazin Россия http://evgeny-lazin.blogspot.com
Дата: 28.03.14 15:28
Оценка:
Здравствуйте, dead0k, Вы писали:

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


L>>А если ф-я lock в той же еденице трансляции? Очень актуально для header only реализаций, как в boost/shared_ptr.

D>А разве есть header-only библиотеки синхронизации?
D>тот-же буст пользует pthread/winapi

В boost/shared_ptr/detail есть простой spin_lock, который полностью header-only и построен на атомарных операциях и барьерах. Обычный mutex может использовать такой подход: spin lock + backoff на обычный mutex, в случае захвата на продолжительное время. И это тоже может быть описано в header-е.
Re[4]: синхронизация потоков
От: tdiff  
Дата: 28.03.14 15:30
Оценка:
Здравствуйте, dead0k, Вы писали:

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

T>>А work() тоже видит flag. Следовательно, по-вашему, оптимизация thread_1_real() невозможна или я что-то недопонял?
D>если work — находится в другой единице трансляции, то да (тут правда может еще сыграть whole program optimization, в энтом деле я не соображаю)

а если
bool flag
заменить на
static bool flag

что-то изменится?
Re[5]: синхронизация потоков
От: dead0k  
Дата: 28.03.14 15:47
Оценка:
Здравствуйте, Lazin, Вы писали:
L>В boost/shared_ptr/detail есть простой spin_lock, который полностью header-only и построен на атомарных операциях и барьерах.
Можно ссылку?
Я просто всегда думал, что атомарные операции и барьеры суть std/crt. Т.е. все равно вызов неких библиотечных функций

t>а если

t>bool flag
t>заменить на
t>static bool flag
static = per-thread, не так-ли? почему бы и не соптимизировать?
Re[6]: синхронизация потоков
От: dead0k  
Дата: 28.03.14 16:20
Оценка:
Здравствуйте, dead0k, Вы писали:
t>>а если
t>>bool flag
t>>заменить на
t>>static bool flag
D>static = per-thread, не так-ли? почему бы и не соптимизировать?
вру конечно, static и per-thread совсем разные.
что касается того почему static bool не даст компилятору соптимизировать и поломать программу — можно предположить, что lock может получить доступ к flag-у через указатели, но тогда компилятор может удостоверится, что в единице трансляции никто не берет адрес flag-а, а значит — можно оптимизировать... так-что в этом вопросе я пас.
Re: синхронизация потоков
От: Pzz Россия https://github.com/alexpevzner
Дата: 28.03.14 16:47
Оценка: +1
Здравствуйте, tdiff, Вы писали:

T>Возьмём классический пример синхронизации потоков:



T>
T>bool flag = false;
T>void thread_1()
T>{
T>    while (!flag) {
T>        work();
T>    }
T>}

T>void thread_2()
T>{
T>    sleep(1000);
T>    flag = true;
T>}

T>

T>В функции work() flag не меняется.

T>Подобный код приводится в качестве примера ошибочного, т.к. может быть оптимизирован компилятором до


Такой код является ошибочным потому, что:
1) в процессе ожидания он впустую грузит процессор
2) компилятор, действительно, может и не переопрашивать флаг. Но конкретно в данном примере он вряд ли это сделает, потому что флаг объявлен, как глобальная переменная, а work() — как вызов внешней функции. Компилятор не может знать, имеет ли внешняя функция доступ к флагу, поэтому будет переепрашивать его после каждого вызова. Однако это было бы не так, если бы флаг был оформлен как локальная для данного файла переменная, либо же функция work() находилась бы в том же файле, и компилятор, проанализировав бы ее, пришел к выводу, что она не меняет флаг.
3) компилятор может переставить местами действия, с целью оптимизации, при условии, что общий смысл не изменится. Например, написано a = 1; b = 2; а компилятор может сделать присваивания в обратном порядке. При этом с точки зрения текущего потока разницы нет, поскольку нет обращений к этим переменным, но вот соседний поток может "увидеть" эти присванивания в обратном порядке, а компилятору на это наплевать. Такую перестановку (обращений к переменным), ровно как и запоминание значения в регистре, можно выключить словом volatile
4) аппаратура тоже может переставить местами действия, и вот это уже словом volatile не выключается, для этого существуют memory barrier'ы.

T>Вопрос: что помешает компилятору и в этом случае оптимизировать чтение значения flag примерно вот так:


Я это уже упоминал — вызов функции, которая делает, с точки зрения компилятора, неизвестно что. Например, меняет флаг.

T>Моё предположение такое: где-то внутри mutex используется какая-то специальная инструкция типа memory barrier, которую компилятор не может позволить себе отоптимизировать.

T>Так ли это? Если да, то что это за инструкция на самом деле?

Сишный компилятор поёт то, что видит. А видит он только текущий исходник и тексты, включенные в него через #include. Поэтому он не может знать, что где-то там в дебрях неизвестной ему функции используется memory barrier.
Re[2]: синхронизация потоков
От: Аноним  
Дата: 28.03.14 19:24
Оценка: +1 -2
Здравствуйте, fdn721, Вы писали:

F>Добавь volatile, и всё будет работать корректно.


Ага, но только под Visual C++ и x86.
Re[2]: синхронизация потоков
От: Аноним  
Дата: 28.03.14 20:30
Оценка:
Здравствуйте, Pzz, Вы писали:


Pzz>Такой код является ошибочным потому, что:

Pzz>3) компилятор может переставить местами действия, с целью оптимизации, при условии, что общий смысл не изменится. Например, написано a = 1; b = 2; а компилятор может сделать присваивания в обратном порядке. При этом с точки зрения текущего потока разницы нет, поскольку нет обращений к этим переменным, но вот соседний поток может "увидеть" эти присванивания в обратном порядке, а компилятору на это наплевать. Такую перестановку (обращений к переменным), ровно как и запоминание значения в регистре, можно выключить словом volatile
А в конкретном примере что может быть переставлено местами?

T>>Вопрос: что помешает компилятору и в этом случае оптимизировать чтение значения flag примерно вот так:

Pzz>Я это уже упоминал — вызов функции, которая делает, с точки зрения компилятора, неизвестно что. Например, меняет флаг.


Ок, тогда немного изменим условия: пускай не bool flag, а static bool flag. Что-то изменится? Или всегда кто-то может угадать адрес flag в памяти и поменять его?

Если дело именно в использовании неизвестной в данной единице трансляции функции, есть ли смысл использовать именно мьютексы при синхронизации доступа к bool?
Серьёзно, почему бы не писать:
bool flag
void thread_1()
{
  while (!flag) {
    sin(123); // внешняя функция, хз что делает, может и флаг поменять - нельзя оптимизировать
    ...
  }
}
Re[3]: синхронизация потоков
От: wander  
Дата: 29.03.14 15:12
Оценка:
Здравствуйте, tdiff, Вы писали:

T>Если не ошибаюсь, в стандарте ++ его вообще нет, так что каждый компилятор ведёт себя, как захочется.

Ошибаешься.
7.1.6.1/7

volatile is a hint to the implementation to avoid aggressive optimization involving the object
because the value of the object might be changed by means undetectable by an implementation. See 1.9 for
detailed semantics. In general, the semantics of volatile are intended to be the same in C++ as they are
in C.

Re[6]: синхронизация потоков
От: Lazin Россия http://evgeny-lazin.blogspot.com
Дата: 30.03.14 07:21
Оценка:
Здравствуйте, dead0k, Вы писали:

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

L>>В boost/shared_ptr/detail есть простой spin_lock, который полностью header-only и построен на атомарных операциях и барьерах.
D>Можно ссылку?
D>Я просто всегда думал, что атомарные операции и барьеры суть std/crt. Т.е. все равно вызов неких библиотечных функций

нет, это либо ассемблерные вставки, либо интринсики компилятора, посмотреть можно в реализации boost/shared_ptr/detail/spin_lock.h
Re: синхронизация потоков
От: imerlin  
Дата: 30.05.14 13:59
Оценка:
Здравствуйте, tdiff, Вы писали:

T>Возьмём классический пример синхронизации потоков:



{...}

T>Подобный код приводится в качестве примера ошибочного, т.к. может быть оптимизирован компилятором до


{...}
T>Такая оптимизация возможна, т.к. компилятор видит, что раз внутри work() flag не меняется, поэтому
T>нет смысла считывать его на каждой итерации.


T>Стандартное в практике решение этой проблемы — использовать mutex:

{...}
T>Моё предположение такое: где-то внутри mutex используется какая-то специальная инструкция типа memory barrier, которую компилятор не может позволить себе отоптимизировать.
T>Так ли это? Если да, то что это за инструкция на самом деле?

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

То есть схема такая:
//Вы создаете событие в обоих синхронизируемых процессах.
HANDLE hEv=CreateEvent(NULL,FALSE,TRUE,"lalala");
//затем один процесс "засыпает" до окончания работы второго:
WaitForSingleObject(hEv);
//и делает что то дальше
//**********************************
//второй процесс чтото делает
{...}
//по окончании говорит, что момент пришел:
SetEvent(hEv) //именно в этот момент "проснется", то есть вернется из функции WaitForSingleObject первый процесс.

Компилятор, естественно, там ничего оптимизировать и испортить не сможет.

Извините, если не понял Вашего вопроса.
Re: синхронизация потоков
От: Abyx Россия  
Дата: 03.06.14 10:18
Оценка:
Здравствуйте, tdiff, Вы писали:

T>Возьмём классический пример синхронизации потоков:


T>
T>bool flag = false;
T>void thread_1()
T>{
T>    while (!flag) {
T>        work();
T>    }
T>}

T>void thread_2()
T>{
T>    sleep(1000);
T>    flag = true;
T>}

T>

T>В функции work() flag не меняется.

T>Подобный код приводится в качестве примера ошибочного, т.к. может быть оптимизирован компилятором до

T> ...

Нет. Этот код является ошибочным, потому что тут есть race condition, наличие которого приводит к UB (undefined behavior).
если есть UB, уже не важно что именно сгенерит компилятор.

Какие-то люди писали выше что достаточно написать volatile. Нет. Volatile не убирает race condition, код по прежнему будет приводить к UB.

Race condition возникает когда "a program ... contains two conflicting actions in different threads, at least one of which is not atomic, and neither happens before the other".
"conflicting actions" — это когда одно из действий — запись.
"happens before" — это когда либо действия выполняются в одном потоке, либо в разных но с мьютексом.

По этому правильные решения — это либо использовать атомарный тип (например std::atomic<bool>), либо использовать мьютекс.
In Zen We Trust
Re[2]: синхронизация потоков
От: Lazin Россия http://evgeny-lazin.blogspot.com
Дата: 04.06.14 21:00
Оценка:
A>Нет. Этот код является ошибочным, потому что тут есть race condition, наличие которого приводит к UB (undefined behavior).
A>если есть UB, уже не важно что именно сгенерит компилятор.

A>Какие-то люди писали выше что достаточно написать volatile. Нет. Volatile не убирает race condition, код по прежнему будет приводить к UB.


A>Race condition возникает когда "a program ... contains two conflicting actions in different threads, at least one of which is not atomic, and neither happens before the other".

A>"conflicting actions" — это когда одно из действий — запись.
A>"happens before" — это когда либо действия выполняются в одном потоке, либо в разных но с мьютексом.

A>По этому правильные решения — это либо использовать атомарный тип (например std::atomic<bool>), либо использовать мьютекс.


вопрос же не об этом был
Re[3]: синхронизация потоков
От: Abyx Россия  
Дата: 04.06.14 21:22
Оценка:
Здравствуйте, Lazin, Вы писали:

A>>Нет. Этот код является ошибочным, потому что тут есть race condition, наличие которого приводит к UB (undefined behavior).

A>>если есть UB, уже не важно что именно сгенерит компилятор.

A>>Какие-то люди писали выше что достаточно написать volatile. Нет. Volatile не убирает race condition, код по прежнему будет приводить к UB.


A>>Race condition возникает когда "a program ... contains two conflicting actions in different threads, at least one of which is not atomic, and neither happens before the other".

A>>"conflicting actions" — это когда одно из действий — запись.
A>>"happens before" — это когда либо действия выполняются в одном потоке, либо в разных но с мьютексом.

A>>По этому правильные решения — это либо использовать атомарный тип (например std::atomic<bool>), либо использовать мьютекс.


L> вопрос же не об этом был


это гадания о том, нужен volatile или нет, вместо того чтобы заглянуть в стандарт, и поискать упоминания volatile в [intro.multithread].
In Zen We Trust
Re[4]: синхронизация потоков
От: Lazin Россия http://evgeny-lazin.blogspot.com
Дата: 05.06.14 09:20
Оценка:
A> это гадания о том, нужен volatile или нет, вместо того чтобы заглянуть в стандарт, и поискать упоминания volatile в [intro.multithread].

Начнем с того, что у ТС-а ни слова нет о новом стандарте, поэтому, рассказывать о том, что тут race и UB — неправильно, в старом стандарте нет ни слова о многопоточности и любая многопоточная программа это UB и что дальше? Продолжить можно тем, что ТС-а интересовало то, почему оптимизации компилятора не ломают код, вопрос интересный, может лучше было найти в стандарте что-нибудь об этом? Я может быть резковат, но это потому, что меня уже порядком достали пуристы от стандартизации. Это С++, детка, UB это не всегда плохо
Re[4]: синхронизация потоков
От: smeeld  
Дата: 05.06.14 10:43
Оценка: -1
Здравствуйте, Abyx, Вы писали:


A> это гадания о том, нужен volatile или нет, вместо того чтобы заглянуть в стандарт, и поискать упоминания volatile.


Скажите, мастер, такой код корректен и работоспособен?

В потоках:


std::unique_lock<std::mutex> lck( mt );

do{

  if(!que.empty()) break;
   run=0; 
    cv.wait(lck); run=1;

      }while(1);



В главном, ожидание:
while(i>0){
   
  mp_t& mp=mp_p[--i];
  
  if(mp.run){  i=num_th; usleep(10); continue; };
   break;
  };

 delete [] mp_p;
};
Re[5]: синхронизация потоков
От: Abyx Россия  
Дата: 05.06.14 10:46
Оценка:
Здравствуйте, Lazin, Вы писали:

A>> это гадания о том, нужен volatile или нет, вместо того чтобы заглянуть в стандарт, и поискать упоминания volatile в [intro.multithread].


L>Начнем с того, что у ТС-а ни слова нет о новом стандарте,

новый это С++14 или тот которому уже несколько лет?

L>Я может быть резковат, но это потому, что меня уже порядком достали пуристы от стандартизации. Это С++, детка, UB это не всегда плохо

а меня достали говнокодеры которые пишут говно с UB, которое перестает работать в новых версиях компилятора, или не работает с другими компиляторами.
еще меня достали всякие отсталые, которые живут в нулевых, а то и в 90х.
In Zen We Trust
Re[5]: синхронизация потоков
От: Abyx Россия  
Дата: 05.06.14 10:50
Оценка:
Здравствуйте, smeeld, Вы писали:

S>Скажите, мастер, такой код корректен и работоспособен?

отформатируй так чтоб его читать можно было.
In Zen We Trust
Re[6]: синхронизация потоков
От: Lazin Россия http://evgeny-lazin.blogspot.com
Дата: 05.06.14 11:08
Оценка:
Здравствуйте, Abyx, Вы писали:

L>>Начнем с того, что у ТС-а ни слова нет о новом стандарте,

A>новый это С++14 или тот которому уже несколько лет?

тот, которому несколько лет

L>>Я может быть резковат, но это потому, что меня уже порядком достали пуристы от стандартизации. Это С++, детка, UB это не всегда плохо

A>а меня достали говнокодеры которые пишут говно с UB, которое перестает работать в новых версиях компилятора, или не работает с другими компиляторами.

ну значит код std::atomic писали такие люди, так как там — UB, если например сделать так:

std::atomic<int> x; 
.... 
x.store(0, memory_order_relaxed);


то там в шаблоне переменной просто значение присвоится и все, прямо как у топикстартера в примере, ну то есть по сути, store(value, memory_order_relaxed) — не более чем аннотация для людей, которые это потом будут читать (на x86)
Re[7]: синхронизация потоков
От: dead0k  
Дата: 05.06.14 11:25
Оценка: +1
Здравствуйте, Lazin, Вы писали:

L>то там в шаблоне переменной просто значение присвоится и все, прямо как у топикстартера в примере, ну то есть по сути, store(value, memory_order_relaxed) — не более чем аннотация для людей, которые это потом будут читать (на x86)

Атомарно же? А больше от relaxed, вроде бы, ничего и не требуется
Re[2]: синхронизация потоков
От: wander  
Дата: 05.06.14 12:01
Оценка:
Здравствуйте, Abyx, Вы писали:

A>Какие-то люди писали выше что достаточно написать volatile. Нет. Volatile не убирает race condition, код по прежнему будет приводить к UB.


Сколько тут было ответов и все почему-то подумали, что предлагалось использовать только volatile.
Между тем, предлагалось добавить volatile к решению с мьютексами (volatile bool + mutex).
fdn721, предложил именно добавить volatile в целях запретить оптимизации с bool флагом (именно об этом (об оптимизации) был вопрос), а не заменить на volatile всю синхронизацию.
Re[7]: синхронизация потоков
От: Abyx Россия  
Дата: 05.06.14 13:26
Оценка:
Здравствуйте, Lazin, Вы писали:

L>ну значит код std::atomic писали такие люди, так как там — UB, если например сделать так:


L>
L>std::atomic<int> x; 
L>.... 
L>x.store(0, memory_order_relaxed);
L>


L>то там в шаблоне переменной просто значение присвоится и все, прямо как у топикстартера в примере, ну то есть по сути, store(value, memory_order_relaxed) — не более чем аннотация для людей, которые это потом будут читать (на x86)


:doublefacepalm: прочитай уже [intro.multithread]. Там написано про и про UB и про relaxed.

Я вообще фигею. Люди не читали даже *вводную* часть стандарта, а сидят тут с умными щами и пытаются что-то доказывать.
In Zen We Trust
Re[3]: синхронизация потоков
От: Abyx Россия  
Дата: 05.06.14 13:41
Оценка:
Здравствуйте, wander, Вы писали:

W>Между тем, предлагалось добавить volatile к решению с мьютексами (volatile bool + mutex).

W>fdn721, предложил именно добавить volatile в целях запретить оптимизации с bool флагом (именно об этом (об оптимизации) был вопрос), а не заменить на volatile всю синхронизацию.

volatile не добавляет коду с мьютексами ничего полезного.
In Zen We Trust
Re[8]: синхронизация потоков
От: Lazin Россия http://evgeny-lazin.blogspot.com
Дата: 05.06.14 13:54
Оценка:
A>:doublefacepalm: прочитай уже [intro.multithread]. Там написано про и про UB и про relaxed.
A>Я вообще фигею. Люди не читали даже *вводную* часть стандарта, а сидят тут с умными щами и пытаются что-то доказывать.

Может уже хватит придираться? Я просто намекнул на то, что relaxed семантика это просто обычные загрузки и сохранения, если ты читал стандарт, то должен и сам это знать. Я лично, отношусь к этому как к аннотации для читателя кода, о чем и написал, ну и где здесь faceplam?
Re[4]: синхронизация потоков
От: wander  
Дата: 05.06.14 14:44
Оценка:
Здравствуйте, Abyx, Вы писали:

A>volatile не добавляет коду с мьютексами ничего полезного.


Ну и что? Речь вообще не об этом. А о том, что совет, пусть и не совсем полезный, был воспринят вообще неверно. В другом, так сказать, контексте.
Re[2]: синхронизация потоков
От: HolyNick  
Дата: 20.06.14 13:05
Оценка:
Где Вы здесь усмотрели race condition (результат выполнения зависит от порядка исполнения потоков)? (пока считаем, что никакой оптимизации нет над переменной)
Re: синхронизация потоков
От: HolyNick  
Дата: 20.06.14 13:29
Оценка:
T>void thread_1_sync()
T>{
T> while (true) {
T> {
T> scoped_lock lock(m);
T> if (flag) break;
T> }
T> work();
T> }
T>}

T>void thread_2_sync()

T>{
T> sleep(1000);
T> {
T> scoped_lock lock(m);
T> flag = true;
T> }
T>}
T>[/code]

Мьютексы здесь вообще не нужны.
Re: синхронизация потоков
От: HolyNick  
Дата: 20.06.14 13:45
Оценка:
T>Вопрос: что помешает компилятору и в этом случае оптимизировать чтение значения flag примерно вот так:
T>
T>void thread_1_sync_real()
T>{
T>    bool f;
T>    scoped_lock lock(m) {
T>        f = flag;
T>    }
T>    while (true) {
T>        if (f) break;
T>        work();
T>    }
T>}
T>


Компилятор не будет так жонглировать кодом: из void thread_1_sync() он никак не сделает void thread_1_sync_real()...Разные функции абсолютно у Вас получились.
Re: синхронизация потоков
От: HolyNick  
Дата: 20.06.14 14:05
Оценка:
Вообще, оптимизация дело опасное и скорее всего ее делают очень аккуратно, без отсебятины (а давай-ка тут все переставлю местами )

ps: Был случай у меня, что код(всякие расчеты и часть на ассемблере) не хотел(что-то портило стек) работать пока не отключен один из флагов оптимизации (в MS Visual Studio). По крайней мере ошибки я у себя так и не нашел, а отключение флага давало 100% рабочий результат.
Re[3]: синхронизация потоков
От: Abyx Россия  
Дата: 20.06.14 15:19
Оценка:
Здравствуйте, HolyNick, Вы писали:

HN>Где Вы здесь усмотрели race condition


race condition может не совсем правильный термин, надо было сказать "data race" — все таки в С++ термин "race condition" не используется.
хотя я не вижу принципиальной разницы.
In Zen We Trust
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.