Как стать автором
Обновить

Комментарии 185

Простите, а в чем смысл этой статьи?

Ну, так в описании самих "засад" потоков и даже, возможно, кода. Вы хоть прочитали-то статью?

К сожалению, прочитал :-(

Поздравляю, если у Вас подобных проблем не возникало. Или на Ваш взгляд засада с циклом - норм?

Но кто мне объяснит, как можно допускать, чтобы какой-то код по-разному проявлял себя в разных режимах проектирования программы - отладки и выпуска?

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

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

Других способов, (кроме тестов), я не знаю. Отпишитесь, если вы найдете какую либо альтернативу тестированию.

Есть всякие санитайзеры. tsan и valgrind, например, ловят некоторые проблемы многопоточности.

Еще можно код размечать - все не локальные потоку переменные надо помечать, как например тут. И там уже статические анализаторы и сам компилятор заметит, если у вас какие-то проблемы есть.

Конечно, это не спасает от всех ошибок, но позволяет многие из них отловить заранее.

Статический анализ кода сюда же. Не все, но что-то может найти.

Все вместе со всей ветки комментариев избавляет от большей части ошибок, но никак не избавляет от дебага неделями в поисках еще одной стреляющей на проде ошибки. Такие ошибки имею свойство проявляться только на проде под нагрузкой.

Есть целая группа разработчикрв которая вообще не "верят" в такие проблемы, т.к. училь работать с компилятором или платформой, их прячущими (Майкросот очень умел в этом).

Такие вот слайс оф лайф я люблю кидать им в лицо, потому что один мой пример их не кчит - вместо этого у меня репутация шамана, от неверия которого код "ломается". В данном случая слайс хоррора, так как автор явно не совсем понимает что происходит.

Одно из замечательных фичей некоторых сборок компиляторов является то, что компилятор замечает неопределенное поведение на выходе из цикла. Неопределенного поведения не бывает, а значит цикл бесконечен. Заглядываешьв сгенерированный код и видишь откровенный jmp вместо jne или чего-то такого.

И я не шучу, это не какой-нибуть стартап а отделы разработки такого гиганта как "Алмаз-Антей".

компилятор замечает неопределенное поведение на выходе из цикла. Неопределенного поведения не бывает, а значит цикл бесконечен. Заглядываешьв сгенерированный код и видишь откровенный jmp вместо jne или чего-то такого.

Я в другой ветке приводил ссылку. Там даже похожий код есть и объясняется, что происходит: https://mohitmv.github.io/blog/Shocking-Undefined-Behaviour-In-Action/

Других способов, (кроме тестов), я не знаю. Отпишитесь, если вы найдете какую либо альтернативу тестированию.

Попрбуйте Clang TSAN - отличная штука, как и Valgrind-helgrind (примерно тоже самое, только намного медленее - если не можете использовать clang). Они в рантайме отслеживают доступ к памяти и дают дельные отчеты - находят ошибки незащищенного доступа, потенциальные локи и т.п.

Лучше всего прогонять такое в CI совместно с тестами, находят весьма заковыристые проблемы.

Чел не осилил многопоток и взорвался

А причём тут Qt? Он у вас в тэгах указан, а об особенностях многопоточности там ни слова.

При том, что все написано на Qt. Класс потока qt-шный и соотвественно реализация потока qt-шная. А засаду с циклом кому адресовать, если не к Qt? Правда, может к компилятору? Ну, и вариант автора , конечно, не исключен. Где на Ваш опытный взгляд тут "собака порылась"?

Специально перечитал все листинги. Ничего про многопоточность в Qt там нет. И опережая ход ваших мыслей скажу что в Qt очень хорошая документация по многопоточности - начинать надо с неё, прежде чем изобретать велосипеды.

Но если немного применить телепатию то можно предположить что вы отнаследовались от QThread и забыли про это написать и привести примеры. В любом случае для описанных примеров, в Qt есть более удобные инструменты.

Друзья, но давайте все говорить по существу.. Если для вас описанные мною проблемы - не проблемы потоков, то не знаю - завидовать вам или огорчаться по вашему поводу.

Это проблемы многопоточности. Про которые написано в любой книге про многопоточное программирование. Которые вам стоит почитать. Или курсы какие-нибудь прослушать. Онлайн их очень много бесплатных. Вместо того, чтобы описывать ваше героическое сражение с ними и ваши "гениальные" их решения. Вот вам аналогия. Выхожу я такой и пишу статью:

Представляете, если сложить 2 миллиарда и 1 миллиард, то программа выводит отрицательное число. Как такое может быть - сложили 2 положительных, а получили отрицательное?! Какие коварные эти большие числа. Я вот стал считать тысячи отдельно и единицы, вот все заработало! Пользуйтесь, кому надо. Правда, если складывать не 2 числа, а 1000, то все опять ломается, но мне это не надо, вроде работает. Вот, кстати, ссылка на мою предыдущую статью (процитированную в формате научной публикации! Ведь я Автор Статьи), где я реализовал деление на 1000 через вычитание в цикле.

Вот какое ощущение создает ваша статья. Вы не знаете основ многопоточного программирования, а взялись писать о нем статью.

Вы не знаете основ многопоточного программирования, а взялись писать о нем статью.

Простите мне мой неприкрытый сарказм, но предыдущая статья автора называлась ни много, ни мало, а "Все секреты многопоточности"

Лично мне не очень понятно, почему надо изучать потоки именно в qt.

Для начала можно с std::thread поразбираться.

Ага, автор ее еще и в списке литературы указал. Фундаментальный труд, не иначе.

БГГ

Меня сейчас сильно подмывает начать писать статью в виде вредных советов "Что компилятор сотворил с моим кодом?" и показать, что получается "неправильный код" в в Compiler Explorer.

Т.е. Мэтт Годболт, только наоборот

Например, при десяти потоках и числе циклов у каждого из потоков равном миллиону (см. также первоисточник рассматриваемой задачи [2]) вместо десяти миллионов выскакивал результат на несколько единиц меньше

Это же в чистом виде race condition. Замените счетчик на std::atomic<int> и проблема исчезнет. Или введите критическую секцию через какие-нибудь мьютексы. Чтобы оно не влияло на производительность, пусть каждый поток считает локальный счетчик, а в конце, через критическую секцию, добавлет его к глобальному счетчику.

Вообще, вы тут статьи про многопоточность пишите и даже их цитируете, как элктронные ресурсы, но для начала почитайте какую-нибудь книжку про многопоточное программирование на C++.

Сделать это совсем просто. Для этого достаточно добавить одну строчку в начало кода потока, где он и будет ждать пока флаг не будет установлен

Для этого придумали события. Писать такие ручные циклы - плохой стиль.

Но кто мне объяснит, как можно допускать, чтобы какой-то код по-разному проявлял себя в разных режимах проектирования программы - отладки и выпуска

Нельзя это допускать. Это обычно говорит об undefined behavior в программе. Состояние гонки, кстати - это UB. При их наличии программа может вести себя по разному даже при разных запусках, а не только при перекомпилировании в разных режимах. По возможности избегайте этого.

Была вот такая статья, например:

https://habr.com/ru/articles/443406/

Для этого придумали события

promise/future не подойдут? Или Вы про event в Windows?

Состояние гонки, кстати - это UB

Имхо, это всё-таки не совсем UB. Состояние гонки обычно возникает не в результате того, что компилятор выдал код, исполнение которого непредсказуемо. Как, например, такое:

int x = 5, y = 6;
int z = x++ + y++; // не уточнено, будет вычислен первым x++ или y++

Многопоточные программы надо писать так, чтобы они работали даже с учётом того, что неизвестно, с какой скоростью потоки запускаются, за какое время дойдёт исполнение до определённого места в процедуре потока и т.д. То есть, надо даже рассчитывать на некоторую неопределённость в исполнении и уметь подстраиваться под неё.

promise/future не подойдут? Или Вы про event в Windows?

Ну да, виндовые события. Но аналогичные вещи есть, наверно, везде. В том же QT нагуглилось QTWaitCondition.

Имхо, это всё-таки не совсем UB. Состояние гонки обычно возникает не в результате того, что компилятор выдал код, исполнение которого непредсказуемо.

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

Ну да, виндовые события. Но аналогичные вещи есть, наверно, везде.

Хотелось бы опираться на что-нибудь из стандарта C++. Виндовые события - это и правда удобно: взвел событие, и на его ожидании сразу много потоков сработали.

 Вы же не можете предсказать, в каком порядке и сколько инструкций от каждого потока выполнет система? 

Если так рассуждать, то вообще любое многопоточное приложение исполняется непредсказуемо, о чем я уже и написал.

в зависимости от опций компиляции и фазы луны программа может вести себя совершенно по разному

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

Если так рассуждать, то вообще любое многопоточное приложение исполняется непредсказуемо, о чем я уже и написал.

Но если не допускать гонок, то этот порядок не влияет на результат.

Edit:

Хотелось бы опираться на что-нибудь из стандарта C++. Виндовые события

std::condition_variable, оказывается, есть. Это ровно оно.

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

Если так рассуждать, то вообще любое многопоточное приложение исполняется непредсказуемо, о чем я уже и написал.

И вообще, документация говорит:

Some examples of undefined behavior are data races, memory accesses outside of array bounds, ...

Имхо, это всё-таки не совсем UB. Состояние гонки обычно возникает не в результате того, что компилятор выдал код, исполнение которого непредсказуемо.

UB - это код, результат которого непредсказуем, поэтому компилятор вправе делать что угодно.

Я в соседней ветке уже привел: документация говорит:

Some examples of undefined behavior are data races, memory accesses outside of array bounds, ...

UB - это наличие в коде некоторых запрещенных конструкций. Из-за чего компилятор может генерировать машинный код, не эквивалентный вашей программе. Не обязательно из-за оптимизаций. Разрабочики гарантировали, что без UB компилятор отработает правильно. Что там с UB получится - они ответственности не несут.

Так, например, из-за состояния гонки операция cnt++ может увеличить cnt на 0, если ее записать в машинном коде. Хотя код на C++ говорит, что увеличение всегда будет на 1.

Люди правильно пишут. Ошибки детские. Прочтите это эталонное изложение (а читать там не много) и подобных ошибок у Вас не будет никогда. Проверено.

2. Дж.Рихтер «Windows. Создание эффективных Win32-приложений с учётом специфики 64-разрядной версии Windows», 2008

главы:

6.  Базовые сведения о потоках
7.  Планирование потоков, приоритет и привязка к процессорам
8.  Синхронизация потоков в пользовательском режиме
9.  Синхронизация потоков с использованием объектов ядра
11. Пулы потоков

Вот такой поиск несколько полезных ссылок выдает.

Еще есть потрясная книжка Anthony Williams - C++ Concurrency in Action Practical Multithreading, с вторым изданием от 2019 года. Покрывает от основ до относительно продвинутых нюансов многопоточностей.

Только желательно с оригиналом сравнивать редактор или переводчик во втором издании местами накосячили. В терминах в основном, но где-то был пропуск еррат. Оригинал был доступен просто так на Manning

Если хотите играться с неатомарными глобальными флагами ожидания, то используйте:

while ( !pFThCounter->bIfLoad )   
   QThread::currentThread()->sleep( 0 ); // "0" заставляет планировщик переключиться на другой поток

И будет всё нормально работать.

Про проблемы многопоточности, изложенные в этой статье, можно почитать отличные труды Даннинга и Крюгера.

Господа, стоило мне отвлечься на какое-то время, как меня тут смотрю помножили на ноль? Прям, печалька... А советов почитать - просто уйма. Но я не про почитать, а про конкретно - почему не работает код (Выпуск) во второй засаде...

Вы все тут умные, ну, очень умные, начитавшиеся множества книжек и советы ваши полезные, но ... только скажу, что в основном совсем не в тему. Похоже, вам нужно (может быть, правда, не всем) начать с элементарных основ и вспомнить и, возможно, по новой усвоить, что такое алгоритм, забыв на мгновение про свои самые крутые языки (в прямом и переносном смысле) . Но это я так - брюзжу в силу возраста. Думаю, что подавляющее большинство из вас еще не родилось, когда я начал программировать. Так что опыт есть. Это так - для справки. Все это время только и занимался программированием и много-много читал. Понимаю, возраст - не аргумент. А с какого-то момента даже наоборот :) Но все же. Нужно заглядывать в профиль, смотреть на тематику статей, хотя бы бегло, чтобы все же понимать на кого "наезжаешь"...  ;)

А теперь - по делу. Имеем элементарнейшую (еще раз - элементарнейшую!) параллельную задачу - узнать/угадать до чего она досчитает быстренько, ну, т.е. параллельно, изменяя одну общую переменную.  И, что тут скажешь, - полились "высокия материи" - гонки, прерывания, события, сообщения, UB и т.д. и т.п. Но если у меня ошибки "детские", то у кого-то они просто ясельного возраста, а может и меньше. Так что именно вам читать - не перечитать. Похоже, вы совсем не понимаете или не врубаетесь сходу в суть проблемы. Ведь, ваша задача - решить проблему, а не "умно породить" или "нагородить" все выше перечисленное - гонки, зависания... А потом бороться со свом же созданием, козыряя своими "скилами". 

Дело в том, что проблема, которая, как говорится, и выеденного яйца не стоит, в переложении на потоки превращается "черти во что"! Это вы не понимаете? Если нет, то мне вас жаль. Для программиста главное - алгоритм. А на каком языке, какие библиотеки - второстепенно. Да вообще не важно даже. Но чем ближе при этом будет решение по форме к исходному алгоритму, тем больший вам респект. Это-то хотя бы понятно? Потоки искажают и извращают решение. Чтобы потом его загнать в какую-то приемлемую форму нужно, ой, как постараться! И именно этим вы гордитесь? Именно в ваши "сети" вы и хотите меня завлечь, советуя разную литературу и т.п.? Спасибо. Это уже давно пройденный этап.

Потоки хороши для реализации изолированных задач. И я написал почему. И даже здесь есть определенные проблемы. Но с точки понятия алгоритма - только изолированные задачи. Тут хотя бы есть шанс, что алгоритм худо-бедно доработает до конца. Если потоки взаимодействующие, то это уже ваши проблемы. У меня таких проблем, какие описаны в статьях, с автоматами просто нет. Чтобы это понимать и знать мне для этого любые самые умные книжки про потоки не нужны. Что нужно, я давно прочитал. А в порядке эксперимента - с вами делюсь своими "успехами". Это для тех, кто не понял суть моих статей. Тот же тест потоков, который рассматривается, показывает, как с потоками элементарнейшая задача (тут даже взаимодействия между потоками нет) превращается в дикий бред. Других слов просто не нахожу.

Итак. Хватит поучать. Если есть конкретные замечания по коду теста, по алгоритму - давайте. Проверю, скажу большое спасибо за совет, за науку... Если нет, то, как говорится "на нет и суда нет".  Код любой хорош, если он приводит к верному результату. Это замечание для особо "умных".

Я, например, абсолютно не понимаю поведение потока/потоков  во второй засаде. Переклинило, можно сказать. На это счет есть какие-то соображения? Вот что хотелось бы обсудить. Вы ж ребята умные? Подскажите в чем проблема? Или как?  Только, еще раз, важно знать почему не работает мой код в первом варианте (с добавленной одной строчкой цикла), а не как переписать код, чтобы он заработал. Это я сделал и без вашего участия. Но от этого легче не стало. А вы врубаетесь почему, коллеги?

Безотносительно всех прочих достоинств вашего, безусловно, поучительного комментария, скажите: как вы умудряетесь в соседних абзацах писать "Хватит поучать" и "Подскажите в чем проблема?".

Тоже безотносительно... Все же есть разница между "поучать" и "подскажите". Поясню на примере. Предположим, человек тонет. Во-вот захлебнется. А Вы на берегу. И вместо того, чтобы броситься к нему на помощь, Вы начинаете его "поучать". Мол, не туда плывешь, не так гребешь... А еще лучше - почитай вот эту книжку, где расписано подробно, как правильно плавать...

Никто Вас не обнулял, и предыдущие статьи у Вас - хорошие. Просто люди думали, что здесь статья, но мы имеем просто вопрос из разряда: "мужики, не работает код, помогите, горит!" с настолько корявым "исследованием" проблемы, что да, от изложенных "методов" работы с потоками, говоря народным языком, "волосы дыбом встают".

Если Вам нужно практическое и быстрое решение задачи "в лоб", то оно очень простое: полностью удалите весь нагороженный "огород", связанный с многопоточностью, кроме запуска потоков, заведите единственную критическую секцию и везде, где пересчитываете и меняете значение переменной пишите:

// заводим переменную либо связанные переменные
int i = 0;
int j = 0;
CriticalSection i_cs;

i_cs->lock();

// читаем
// пресчитываем
// записываем переменную

i += 8;

// или делаем с ней что хотим

j = i - 0 + 0 + i*2;

i_cs->unlock();

То есть такие куски алгоритма дожны быть защищены критической секцией.

Если есть несколько связанных между собой пременных, то вычисляться и меняться они должны в пределах одной критической секции (см. пример выше).

Если где-то необходимо только читать значение любой из защищённых переменных, пишете так:

int moment_i = 0;

i_cs->lock();
moment_i = i;
i_cs->unlock();

// используем значение moment_i, но использовать его для пересчёта и дальнейшей записи в i уже нельзя (i за время расчётов могла измениться)

int d_blah_blah_blah = moment_i*moment_i;

Да просто объявить переменную не как int i, а std::atomic_int i, и даже критических секций не нужно. Случай простой совсем.

Не совсем. Там помимо счетчика еще есть проблемы, вроде обновлений QLabel из всех потоков одновременно.

Тогда критической секцией QLabel защитить.

Нельзя. Представьте, сколько времени тратится на эту операцию.

Плюс уже было введено std::atomic_int (что вообще очень сомнительное решение, ибо на атОмиках - защиту группы переменных не построишь). Вот и пошёл расти "огород", который, поверьте, опять имеет в оконцовке "засаду".

PS О том и речь: без чтения литературы по основам многопоточного программирования - будет "засада" на "засаде".

Представить не могу, т.к. на QT не пишу.

ибо на атОмиках - защиту группы переменных не построишь

Знать бы постановку задачи целиком. Одну переменную защитить можно, а надо ли группу защищать в данном конкретном случае, я не понял.

который, поверьте, опять имеет в оконцовке "засаду"

Какую засаду и почему?

без чтения литературы по основам многопоточного программирования - будет "засада" на "засаде"

ИМХО, вряд ли можно создать литературный труд, чтение которого защитит от всех возможных подводных камней в многопоточном программировании. Например, по каким-то общим советам можно победить гонки и зависания, но получить проблемы с производительностью. А там уже и до корутин и многопоточности без блокировки недалеко.

Одну переменную защитить можно, а надо ли группу защищать в данном конкретном случае, я не понял.
Какую засаду и почему?

Две причины: 1) состояние объекта в 99,99% случаев не хранится в одной переменной, а атомики не позволяют блокировать сразу группу, 2) при пересчёте переменную необходимо блокировать на всё время её пересчёта, ведь другие потоки могут перезаписать её значение, поэтому такое использование атомиков даже если пересчитываемая переменная одна делает алгоритм заведомо ложным. Отсюда на атомиках конечного автомата не построишь. Они хороши в случае несвязанных переменных статистики, и даже при подсчёте ссылок на объёкт (с учётом того, что по достижении нуля ссылка больше никогда не увеличится, и объект должен быть уничтожен).

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

ИМХО, вряд ли можно создать литературный труд, чтение которого...

Так вот, чтобы не создавать и не читать "литературный труд", "литературный плод" или бесплодие, и было, как говорят в народе "не на дурку", и не ради "трёпа языком" предложено вот это. Ключевое выделю жиным:

(с) а читать там не много
https://habr.com/ru/articles/821283/comments/#comment_26926865

Подождём, когда в конец измученный "мы пойдём своим путём" автор вернётся к этому. А он вернётся. Без вариантов.

 атомики не позволяют блокировать сразу группу

Я одного не понял: с чего Вы решили, что я где-то целую группу предлагаю атомиком блокировать.

В атомик можно, действительно, засунуть подсчет какой-то статистики или признак "надо завершить работу". Я вроде с этим нигде и не спорил.

И это не та хорошая "ненормальность", который подразумевает данный раздел

О каком разделе речь? О теме данной статьи?

Так я не про Вас. :)

Я про то, что у человека принципиально неверный подход, и он на своих "шишках" хочет убедиться в этом.

Собственно, почему нет?! Каждый имеет на это право. :)

PS Так же, пользуясь случаем, разъясню, почему в комментариях мало кто оказывает квалифицированную помощь автору.

Специалист, начав читать статью из таких "решений" сразу её закрывает, ибо с первых строк понятно, что далее - "тема для разговора отсутствует". И я не закрыл лишь потому, что подписан на автора, сам оформляю алгоритмы автоматами (это единственно верный способ оформления алгоритмов ака "написания программ"), поэтому автор мне симпатичен.

Проверил. Работает. Причем в более чем в 8 раз быстрее.

Чудны дела твои Господи ;)

Кто еще что может предложить?

Считать в локальном счетчике. И только по завершению потока агрегировать его результат в общую переменную (тут уже не важно, с мьютексом или с атомиком).

Это не то. Цель - проверить работу все же с общей переменной.

Кстати, QAtomicInt (см. рекламу ниже ;) тоже работает. На "соточку", правда, получается медленнее, чем std::atomic_int, но в целом тоже неплохо.

Так что это еще один вариант. Уже набралась коллекция :) Неплохо "засада" разрешается...

Цель - проверить работу все же с общей переменной.

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

А можно мне ответить на вопрос. Есть переменная, 10 потоков в эту переменную без мютексов начинают писать свое. Это на C++ вызывает панику или нет? Просто на golang – 100% паника. Я ради праздного любопытства для себя сравниваю C++ и Golang

С++ абсолютно параллельно что вы делаете со своей переменной, хоть 100500 потоков пусть её пишут читают.

Интересный нюанс, т.к получается теорию надо знать очень хорошо и понимать структуру. Если в Golang при попытке сделать данное деяние ты получишь панику с описанием что попытка перезаписать то что перезаписывается другим потоком. Но в C++ получается ты должен это сам изначально понимать и если опыта нет, то долго потом будешь как дурак искать ошибку.

Спасибо

Но в C++ получается ты должен это сам изначально понимать и если опыта нет, то долго потом будешь как дурак искать ошибку.

C++, к сожаллению, полон таких вещей. При той же работе с памятью можно запросто себе в ногу выстрелить.

Выше уже давал ссылку на статью, где описаны штук 20 способов выстрелить себе в ногу при работе с потоками.

Но в C++ получается ты должен это сам изначально понимать

С++ произошёл от С, а он от ассемблера не далеко ушёл. Поэтому если нет желания вникать во что превращается код на С/С++, то лучше писать на языках которые скрывают от разработчика любые отсылки к памяти как к ячейкам а не как к объектам, и к процессору как исполнителю конструкций языка а не машинного кода.

То что С++ сам что то делает, иногда без указания разработчика, например вызывает деструктор, то это можно считать допустимой помощью, но всегда надо при этом помнить во что может вылиться этот деструктор. Или обращение к памяти через разыменование указателя. Какой нибудь *(unsigned short)(0xB800) = 0x0CAB не должен приводить к нервному тику.

Хороший язык хорошо все скрывает, чтобы не было необходимости копаться в "потрохах" У меня был проект под Linux, когда я работал на разных машинах, Работа велась на windows, а один и тот же код работал и там и там. Чуть повозился с настройками среды, а потом все работало как часы. И все это благодаря кроссплатформенности Qt. Вот тогда я ее и одновременно C++ и заценил, уйдя с VS ;)

Непонятно, почему вы считаете его нехорошим, это же переносимый ассемблер. Мне как начинавшему с машинных кодов PDP-11 и MACRO-II он очень понятен и предсказуем. Требовать от него поведения Java не стоит.
C Qt не всё так радужно под разными платформами, хотя после 20 лет писанины на Qt, можно сказать спасибо что он есть, но сейчас я предпочитаю Java.

А почему в Go будет паника? Мы же без '-race' параметра вызываем. Просто будет непонятно что в итоге записано

Вы попробуйте перезаписывать переменную и увидите панику как только будет момент когда 2 потока попытаются перезаписать её в один условный момент

Работа с atomic_int в C++ почему не атомарна? Это же не большая структура какая-нибудь.

Или Вы про просто int?

Да, я про просто int и вообще про встроенные типы. Именно поэтому есть аж специальный atomic<int>.

Да конечно. Я, конечно, слушаю, но только у меня есть своя голова и какой ни есть, но опыт... В том числе и слушать других;)

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

Еще раз, просто я параллельно программирую, мягко говоря, несколько иначе (все это прошло жесткое испытание практикой) и потоки не использую . По крайне мере, так было до этого. Буду ли я использовать потоки, честно, - не знаю. Сейчас, например, я их попробовал и ... пока отказался внедрять в реальный процесс. Но, зато, по ходу их изучения к автоматному параллелизму добавил "таймерный". Эффект, как и от потоков, но гораздо проще понять, как все это работает, а ядро ВКПа разгружает. Т.е. это элемент определенной технологии. Потоки все же, думаю, буду использовать, но ... с осторожностью. И не сразу сейчас. Особенно в случаях, когда нужна будет скорость.

Пока буду слушать и экспериментировать и делиться результатами этого процесса ;). В этот раз на удивление было интересно. Главное "засада" была пройдена. Пусть по началу шло туговато, но потом "разогрелись". За это огромное всем. Даже тем, кто "уперто" минусует. Но это они так завидуют ;)

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

Можно буду душным. Какие такие ситуации когда надо сделать намеренно гонку данных. Я сидел думал и не придумал.

Получается программа рандомайзер...

Вся программа в 8 раз быстрее заработала? Или что?

Да, конечно. Здесь вся программа - это, ведь, просьто работа со счетчиком. Но ускорилась, конечно, потому, что не нужна стала работа с мютексом.

Спасибо за совет, но пока подобной проблемы нет.

Так что опыт есть

Никто не является опытным во всех областях сразу. Встречали каких-нибудь гениальных математиков, которые отрицают теорию эволюции или, там химиков с нобелевской премией, изобретающих вечные двигатели? Похожие примеры есть вполне. Так что это не аргумент. Опыта параллельного программирования у вас, очевидно, нет.

Имеем элементарнейшую (еще раз - элементарнейшую!) параллельную задачу - ... параллельно, изменяя одну общую переменную

Ну вот вы заблуждаетесь. Это не элементарно. Вы про машинные инструкции, ассемблер, как работает процессор, с высоты вашего великого опыта - навреняка знаете отлично же, да? Ну вот один поток, чтобы увеличить переменную, загружает в регистр значение (X) из памяти. Увеличивает его и записывает назад X+1. Но! До этой записи второй поток может успеть тоже прочитать это же значение X в свой регистр. Увеличить его и потом переписать значение в памяти на X+1. В итоге оба потока сделали свои +1, но переменная изменилась с X на X+1. Вот откуда у вас первая засада.

Но если у меня ошибки "детские",

Параллельное программирование - это весьма сложная область и там действительно много засад. До которых даже с вашим опытом иногда не додуматься. И это все уже отлично решено и разжевано в любых курсах по параллельному программированию. Ваши ошибки выдают в вас человека к этой области вообще не имеющего отношения. Ну как, опять же, великий филолог приходит и начинает доказывать на какой-то своей никому непонятной нотации, почему у всех кругов длина линии примерно в 3 раза больше его размера. Его отправляют читать школьный учебник про Пи, а он отвечает: Да у меня нобелевка по литературе, вы еще не родились, когда у меня бестеллер был. Ну не та область у вас была. Тут ваш опыт не работает.

И, что тут скажешь, - полились "высокия материи" - ... UB

Вы пишите на C++ и UB для вас - это высокая материя? Серьезно? Это основа основ, это как аквалангистам никогда не задерживать дыхание, как строителям каску носить. Вы, видимо, теоретик. Ограничтесь псевдокодом и не трогайте C++ пока.

Для программиста главное - алгоритм. А на каком языке, какие библиотеки - второстепенно.

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

Конкретно в вашем случае: в описании алгоритма все операции выглядят атомарными. Ну есть поток. Если он делает "увеличить counter на 1", то он его на 1 и увеличит, что бы там про другие потоки не написано. Но на языке с++ - это не так, я вам выше уже описал, почему. В языке python, с его Global Interpreter Lock - это уже ближе к правде. В языке Rust гонка данных вообще невозможна, но иногда вам придется ваш алгоритм с ног на голову переворачивать, что бы его на rust написать вообще можно было.

Я, например, абсолютно не понимаю поведение потока/потоков  во второй засаде.

Вы же такой опытный! Мы же все сосунки тут по сравнению с вами. Как же так вам что-то может быть непонятно вообще? И как в этом случае нам, несчастным, что-то тут помыслить? Вам надо в академию наук писать какую-нибудь, а не в наш детский сад. (конец сарказма)

Какая засада у вас там вторая по счету? Виснущие потоки? Где можно посмотреть весь код, а не отдельные его куски без контекста?

Так что это не аргумент. Опыта параллельного программирования у вас, очевидно, нет.

Вы не поверите, но есть ;) Просто этот опыт ни как не связан с потоками. Здесь просто один "тонкий" момент. Программирование с потоками ,строго говоря, не является параллельным. Автоматное программирование - это параллельное программирование.

Странно, не правда ли? Но формально так оно и есть. Ну, я на это тему много уже написал ;) Скажете - "контора пишет"? Мне можете не верить, но математике не верить нельзя... В ней все базируется на формальном доказательстве. Это, чтобы все было объективно, а не наоборот.

Докажете формально, что потоки параллельная модель - даже извинюсь ;) Нет - Ваш ход...

Докажете формально, что потоки параллельная модель - даже извинюсь ;

Файспалм. Потоки не параллельно работают? Ну, может на каком-нибудь хламе 20-ти летнем. Если у вас процессору меньше 18 лет, то там хотя бы 2 ядра-то есть.

И потом, даже если они на одном ядре друг дружку вытесняют, то все равно работа идет параллельно. Цитата из вики "Параллельные вычисления":

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

Ну хорошо, давайте я уточню свой изначальный комментарий: опыта многопоточного программирования у вас нет. Вы до сих пор, похоже, никак не можете осознать, что такое "гонка данных" и почему это плохо.

Ну хорошо, давайте я уточню свой изначальный комментарий: опыта многопоточного программирования у вас нет. Вы до сих пор, похоже, никак не можете осознать, что такое "гонка данных" и почему это плохо.

Спасибо за уточнее... Теперь примерно то ;)

Да я с "гонок данных" начал свою работу (начиная с задачи про RS-триггер) в параллельном программировании. См. например мою статью про Машу. В моделировании схем гонок выше крыши. И надо, чтобы это все работало. И, ведь, работает! Главное, - хорошо. На потоках такое написать - дуба дашь! ;)

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

Да ну, ерунда, и не доказательство вовсе.

Где формулы, теоремы, леммы, вероятности, где математика?

Мне можете не верить, но математике не верить нельзя...

Предоставьте формальное доказательство, что в эйфории от всего этого у вас не привстал. Нет? Ну вот и все.

Предоставьте формальное доказательство, что в эйфории от всего этого у вас не привстал. Нет? Ну вот и все.

C Вами все понятно. Вам не до теории. Просто Вы другим озабочены... ;)

Вам не до теории.

Да и вам тоже. Все мы прочитали множество книжек, каждый свою.

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

Мол, не туда плывешь, не так гребешь... А еще лучше - почитай вот эту книжку, где расписано подробно, как правильно плавать...

Да и вам тоже. Все мы прочитали множество книжек, каждый свою.

Вы не представляете, как Вы правы ;) Я, действительно, теорией уже давно не увлекаюсь. Прошел этот период. Да и не только, видимо, у меня. Теорией сейчас не увлекаются даже теоретики. Такие времена. Такие условия. Но есть один момент, даже два. Свою теорию я все же создал. Это первый момент. Второй момент - я же ее и реализовал. А это, может еще важнее, чем сама теория (теорий много, а вот работающих из них - мало) Т.е. работает и теория, как теория, и она же подтвержена практикой. Что еще надо для ... "счастливой и спокойной старости", как примерно так говаривал один персонаж?

Но в Ваших рассуждениях есть один пробел - "каждый свою". Это плохо. Конечно, было бы хорошо, если бы была одна книжка. Но этого, к сожалению, нет. Поэтому среди многих надо выбирать правильные книжки. Я все же "в детстве правильные книжки читал". Пришлось, правда, прочитать и не правильные. Это те, которые про многопоточный параллелизм ;) Тут, главное, не потерять нить... правильности. А это опять должно быть правильное образование, как тут сказали "классическое советское" К счастью, именно такое нам и давали.

Сейчас у меня появилось время как-то систематизировать и зафиксировать то, что я когда-то сделал, но как-то не было времени им плотно заняться - теорию подчистить, практику (в форме ВКПа) избавить от ошибок. И тьфу-тьфу пока получается. Занимаюсь любимым делом... Почти что хобби... Надеюсь что кому-то и когда-то пригодится. Но для этого надо "ему" будет включить мозги... Это самое тяжелое. По себе знаю. ;)

Так что то, о чем я пишу фактически нет ни в одной книжке. Хотя иногда меня и упоминают.. Так что, если все же хотите понять мои идеи, то они только в моих статьях. Все остальное - обычно их изврат. А правильно - только в авторском исполнении. Так что делайте вывод. ;) Я не хвалюсь - мне это не надо. Но адекватного пересказа своих идей я еще не встречал. Надеюсь, что еще до живу до этого :)

Ну, главное, что мажется, а "советское" мажется особливо, так сказать, хорошо ;)

Как говаривал Пол Фейерабент: "да кто мы все такие, чтобы говорить вам что правильно, а что нет, да и вы-то кто, чтобы говорить нам?"

К счастью, именно такое нам и давали.

А как же обязательная для летнего чтения классическая литература царского периода?

Надеюсь что кому-то и когда-то пригодится. Но для этого надо "ему" будет включить мозги...

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

Как известно, чтобы сексом заниматься, особых мозгов не нужно, а нравится он даже а-ка-демикам.

А как же обязательная для летнего чтения классическая литература царского периода? ...

Это Вы все куда "мажете" или такой "супертонкий" юмор?

На 286 :) Это скорее 30 лет Вот и я с такими как он работаю.. "Не бывает одновременного доступа", "достал ты со своим UB"

Виснущие потоки могут быть результатом создания бесконечного цикла в следстие UB

Потоки не параллельны только если у вас одноядерный процессор.
Если больше одного реального (не HT) ядра -- вполне параллельны, но не непрерывно параллельны, шина данных пока ещё не может одновременно читать и писать.

Да даже с одним процессором они будут выполнятся конкурнетно, а не "сначала весь один, потом весь другой". И все те же проблемы с гонками данных и дедлоками остаются точно такие же.

Плюсую. Кстати тоже заблуждение у людей которые занимаются многопоточным исполнением. Они создают условно программу где есть 4 потока и заключение: эта программа должна работать на 4 ядрах чтобы все было хорошо и быстро. А потом оказывается что утилизация самих вычислений настолько мизерная что на одном ядре это все заводится и никаких проблем с производительностью нет...а часто она еще больше, т.к ОС тоже нужно время чтобы распихать потоки по разным ядрам.

Конкурентно, это значит что они будут прерываться во времени, вытесняя друг друга.
Я говорю о другом, в один момент кванта времени у двухядерного процессора, физически параллельно выполняются два потока, до тех пор пока один из потоков не начнёт обращаться к памяти, тут арбитраж шины данных разрешит либо чтение из памяти либо запись. Т.о. один из потоков однозначно будет ждать освобождения шины.

Этот опыт легко сделать если в начале и в конце потоков запретить планировку и обернуть код в критическую секцию.

Это намек на то что процессор выполняет все инструкции по очереди? или я неправильно понял? Если да, то это слишком душно.

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

Это намек на то что процессор выполняет все инструкции по очереди? или я неправильно понял? Если да, то это слишком душно.

Более того, даже этот намек - наивное заблуждение.

Это было правдой пару десятилетий назад. Сейчас, на процессорах с глубоким пайплайном и микрооперациями, даже одно ядро выполняет кучу операций параллельно. Пока одна читает данные из кеша, другая гоняет свои на АЛУ, а третья декодируется, например. Иначе как год от года растет количество операций на такт?

Кстати, очень хороший пример это игры. Игровые процессоры имеют большую частоту на ядро, серверные имеют порой в 10-ки раз больше ядер чем в игровых, но меньшую частоту. Если пойти по математике, то серверный проц ставит на лопатки при формуле [частота] x [кол-во ядер]

Но, когда ты запускаешь игру оказывается что игры заточены на маленькое количество ядер и зачастую там 2-4 CPU используется и это потолок (думаю это связано с тем самым параллельным вычислением и сложностью синхронизации). И при всем при этом параллельности как бэ нет (по мнению автора), но в игре происходит гиганское количество событий(вычислений) одновременно, хотя по логике их должно быть не более чем [количество ядер] в использовании. Магия видимо

Наверное, лет десять назад написал тест - жуки (есть такая задачка в имитационном моделировании). Здесь 7 жуков гоняются друг за другом. Соединены между собой линиями, которое динамически меняют цвет. Так получился некий "скринсайвер". Можно разгонять до 300 жуков. Каждый жук - автомат. Они взаимодействуют дгуг, с другом, жрут, если догоняют другого, рождаются новые жуки и в результате создается такая картинка.

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

Но если Вы сделали многопоточное приложение, процедуры разных потоков на современном процессоре (с вероятностью почти 100% - любом) будут выполняться реально одновременно. Вот в таких случаях и возникают проблемы с одновременным доступом к данным из нескольких реально параллельных процессов, выполняющихся одновременно.

OK. А хотели-то чего?

почему не работает мой код в первом варианте (с добавленной одной строчкой цикла)

Потому что внутри цикла нет обращения к ОС, и планировщик отдаёт потоку максимум кванта времени, это глухой цикл, и он нагрузит ядро процессора на максимум. Остальным потокам в этом процессе будет тяжко получить свой квант.
А вот внутри большого цикла есть и работа со строками, а это в Qt однозначное выделение памяти системным вызовом, так и usleep, который точно положит поток в очередь ожидания. Поэтому этот вариант работает а первый нет.
Я уже написал, как избавляться от глухого цикла ожидания флага в потоке.
Но ещё лучше освоить:

это не фотошоп
это не фотошоп

мой опыт  и я

Но одно  эти

Ошибки, правда,  были

реанимирован  аналогичный

после его изменений  сделать

со счетчиком.   Но как это

чем  ошибки

когда  искусственный

Который пост подряд читаю и не пойму никак - это тренд в IT какой-то новый, двойные пробелы вставлять?

Надо наматывать на ус, а то отстану от темы и не хайпану среди молодежи...

Думаю, что подавляющее большинство из вас еще не родилось, когда я начал программировать.

Атец, вполне возможно, что когда нас еще не родилось, а вы начинали программировать, и потоков-то не было.

Если вы перфокарты помните и в руках их держали, мое вам почтение, но с потоками вам, может, не надо, а?

Да и "крайняя статья" как-то ударила по глазам. Кто так говорит?

Парашютисты ;)

Про свои статьи?)

Про всё!

У них есть выражение крайний прыжок, чтобы не говорить последний

main.cpp
#include "mainwindow.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}

mainwindow
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include "SetVarThread.h"
#include <QElapsedTimer>
#include <QMutex>
#include <QSemaphore>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    void Process();
    void Reset();

    void AddCounter();
    void AddCounterMx();
    void AddCounterSem();
    void DecrementActive();

    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

    int pVarNumberOfThreads{10};
    long nMaxValue{100000};   // максим. значение общего счетчика
    bool pIfSemaphoreLotThreads{false};
    bool pIfMutexLotThreads{true};
    QString pVarStrSleepLotThreads;
    QMutex  m_mutex;
    QSemaphore  m_semaphore;

    CSetVarThread   *pCSetVarThread;
    int pVarTimeLotThreads{0};
    long pVarExtrCounter{0};
    void ViewConter(int n);
    void ViewTime();
    QElapsedTimer timeLotThreads;
    bool bIfLoad{false};
    int nIdTimer{0};

    virtual void timerEvent(QTimerEvent*);
private slots:
    void on_pushButtonReset_clicked();

    void on_checkBoxMutex_clicked();

    void on_checkBoxSemaphore_clicked();

    void on_lineEditMaxValue_editingFinished();

    void on_lineEditNumberOfThreads_editingFinished();

    void on_lineEditSleep_editingFinished();

private:
    Ui::MainWindow *ui;
    friend class ThCounter;
};
#endif // MAINWINDOW_H

#include "ThCounter.h"
#include <cmath>
#include <stdio.h>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    nMaxValue = 100000;   // максим. значение общего счетчика
    pVarNumberOfThreads = 10;
    ui->checkBoxMutex->setChecked(pIfMutexLotThreads);
    ui->checkBoxSemaphore->setChecked(pIfSemaphoreLotThreads);
    ui->lineEditSleep->setText("");
    nIdTimer = startTimer(10);
    Process();
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::ViewConter(int n) {
    string str = QString::number(n).toStdString();
    ui->lineEditCounters->setText(str.c_str());
}

void MainWindow::ViewTime() {
    string strTime = QString::number(timeLotThreads.elapsed()).toStdString();
    ui->lineEditTime->setText(strTime.c_str());
}

void MainWindow::Process() {
    bIfLoad = false;
    ui->lineEditMaxValue->setText(QString::number(nMaxValue).toStdString().c_str());
    ui->lineEditNumberOfThreads->setText(QString::number(pVarNumberOfThreads).toStdString().c_str());
    pCSetVarThread = new CSetVarThread();
    int i;
    for (i=0; i<pVarNumberOfThreads; i++) {
        CVarThread var;
        var.pQThread = new ThCounter(&nMaxValue, this);
        string str = QString::number(i).toStdString();
        var.strName = str;
        pCSetVarThread->Add(var);
    }

    timeLotThreads.start();
    pVarExtrCounter = 0;
    TIteratorCVarThread next= pCSetVarThread->pArray->begin();
    while (next!=pCSetVarThread->pArray->end()) {
         CVarThread var= *next;
         var.pQThread->start(QThread::Priority(0));
         pCSetVarThread->nActive++;
         next++;
    }
//    pVarExtrCounter = 0;
    bIfLoad = true;
    ui->lineEditTime->setText("");
    ui->lineEditCounters->setText("");
}

void MainWindow::Reset()
{
    pVarExtrCounter = 0;
    bIfLoad = false;
    // удалить потоки
    TIteratorCVarThread next= pCSetVarThread->pArray->begin();
    while (next!=pCSetVarThread->pArray->end()) {
        delete next->pQThread;
        next++;
    }
    if (pCSetVarThread) {
        pCSetVarThread->pArray->clear();
        delete pCSetVarThread;
    }
    pCSetVarThread = nullptr;

    ui->lineEditTime->setText("");
    ui->lineEditCounters->setText("");

}

void MainWindow::on_pushButtonReset_clicked()
{
    Reset();
    Process();
}
QMutex  g_mutex;
void MainWindow::DecrementActive() {
    g_mutex.lock();
    pCSetVarThread->nActive--;
    g_mutex.unlock();
    if (!pCSetVarThread->nActive) {
        ViewConter(pVarExtrCounter);
        ViewTime();
    }
}

void MainWindow::AddCounter() { pVarExtrCounter++; }

void MainWindow::AddCounterMx() {
        m_mutex.lock();
        pVarExtrCounter++;
        m_mutex.unlock();
}

void MainWindow::on_checkBoxMutex_clicked()
{
    pIfMutexLotThreads = ui->checkBoxMutex->isChecked();
}

void MainWindow::on_checkBoxSemaphore_clicked()
{
    pIfSemaphoreLotThreads = ui->checkBoxSemaphore->isChecked();
}

void MainWindow::on_lineEditMaxValue_editingFinished()
{
    QString str = ui->lineEditMaxValue->text();
    nMaxValue = str.toInt();
}

void MainWindow::on_lineEditNumberOfThreads_editingFinished()
{
    QString str = ui->lineEditNumberOfThreads->text();
    pVarNumberOfThreads = str.toInt();
}

void MainWindow::timerEvent(QTimerEvent* t) {
    Q_UNUSED(t)
    if (t->timerId() != nIdTimer)
        return QObject::timerEvent(t);
    string strExtrCounter = QString::number(pVarExtrCounter).toStdString();
    ui->lineEditCounters->setText(strExtrCounter.c_str());
    if (pCSetVarThread->nActive != 0) {
        string strTime = QString::number(timeLotThreads.elapsed()).toStdString();
        ui->lineEditTime->setText(strTime.c_str());
    }
}

void MainWindow::on_lineEditSleep_editingFinished()
{
    pVarStrSleepLotThreads = ui->lineEditSleep->text();
}

CSetVarThread
#ifndef CSETVARTHREAD_H
#define CSETVARTHREAD_H
#include "VarThread.h"

class CSetVarThread
{
public:
    CSetVarThread();
    virtual ~CSetVarThread();
    bool Add(CVarThread &var);
    TArrayCVarThread *pArray;
    int nActive{0};
};

#endif // CSETVARTHREAD_H
#include "SetVarThread.h"

CSetVarThread::CSetVarThread()
{
    pArray = new TArrayCVarThread();
}

CSetVarThread::~CSetVarThread()
{
    if (pArray)
        if (pArray->size() != 0)
            pArray->erase(pArray->begin(), pArray->end());
    delete pArray;
}

bool CSetVarThread::Add(CVarThread &var) {
    TIteratorCVarThread next=find(pArray->begin(), pArray->end(), var);
    if (next==pArray->end()) {
        pArray->push_back(var);
        return true;
        }
    else {
            return true;
    }
}

Код потока.

ThCounter
#ifndef THCOUNTER_H
#define THCOUNTER_H
#include <QThread>
#include <QTimer>

class MainWindow;
class ThCounter: public QThread
{
public:
    explicit ThCounter(long *pIntMax, MainWindow *parent = nullptr);
    virtual ~ThCounter();

private:
    void run();
    int nMaxValue{0};

    bool bIfRun{false}; // флаг работы потока
    MainWindow *pFThCounter{nullptr};
};

#endif // THCOUNTER_H

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "ThCounter.h"

ThCounter::ThCounter(long *pIntMax, MainWindow *parent):
    QThread()
{
    pFThCounter = parent;
    nMaxValue = *pIntMax;
    bIfRun = true;
}

ThCounter::~ThCounter(void) {
    bIfRun = false;
    quit();         // корректный способ завершения потока
    wait(3000);         // ожидание завершени потока (неограниченное ожидание wait())
                    // подробнее см. Боровский А. Qt4.7... стр.170
}

void ThCounter::run() {
    while (!pFThCounter->bIfLoad) {
//        if (!bIfRun) break;
    }
    int n=0;
    while (n<nMaxValue && bIfRun ) {
//        if (pFThCounter->bIfLoad) {
            bool bMx = pFThCounter->pIfMutexLotThreads;
            if (bMx) {
                pFThCounter->AddCounterMx();
            }
            else pFThCounter->AddCounter();

            QString qstr = pFThCounter->pVarStrSleepLotThreads;
            if (qstr.length()>0) {
                int nSleep = qstr.toInt();
                usleep(nSleep);
            }
            n++;
//        }
    }
    pFThCounter->DecrementActive();
}

CVarThread
#ifndef CVARTHREAD_H
#define CVARTHREAD_H
#include <list>
using namespace std;
#include <QThread>

class CVarThread
{
public:
    CVarThread(string &nam);
    CVarThread(void);
    CVarThread(const CVarThread& var);

    QThread *pQThread{nullptr};

    string		strName{""};			//

    bool operator==(const CVarThread &var) const;
    bool operator<(const CVarThread &var) const;
    bool operator!=(const CVarThread &var) const;
    bool operator>(const CVarThread &var) const;
    CVarThread& operator=(const CVarThread& var);


    bool    IfEqu(CVarThread& var);
};
typedef list<CVarThread> TArrayCVarThread;
typedef list<CVarThread>::iterator TIteratorCVarThread;

#endif // CVARTHREAD_H

#include "VarThread.h"

CVarThread::CVarThread(string &nam)
{
    strName = nam;

}

CVarThread::CVarThread(void)
{
    strName = "";
}

CVarThread::CVarThread(const CVarThread& var)
{
    strName	= var.strName;
    pQThread = var.pQThread;
}

bool CVarThread::IfEqu(CVarThread& var)
{
    if (strName != var.strName) return false;
    return true;
}

bool CVarThread::operator==(const CVarThread &var) const
{
    if (strName==var.strName) return true;
    else return false;
}

bool CVarThread::operator<(const CVarThread &var) const
{ return strName<var.strName; }

bool CVarThread::operator!=(const CVarThread &var) const
{ return strName!=var.strName; }

bool CVarThread::operator>(const CVarThread &var) const
{ return strName>var.strName; }

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

Что касается параллельного программирования:

При pIfMutexLotThreads == false , MainWindow::AddCounter как раз содержит гонку данных. Вы, видимо, это заметили, поэтому ввели MainWindow::AddCounterMx

Но в MainWindow::ViewConter вы этого не заметили. Вы там вызываете QLabel::SetText (скорее всего, QLabel, но в приведенном коде тип lineEditCounters не приведен). ViewConter вызывается из MainWindow::DecrementActive , который вызывается из каждого потока (Тут, кстати, опечатка: должно быть ViewCounter). Но SetText, как и многие другие методы в QT - не thread safe. Это может привести к тому, что у вас в текстовом поле останется ненулевое число, даже если все потоки завершатся. Вам покажется, что программа повисла. Это одно из объяснений вашей засады. Или у вас там что-то другое? Как вы вообще определяете, что потоки виснут?

Правильный подход - это вызывать SetText через InvokeMethod, тогда проблем не будет.

Это может привести к тому...

Судя по коду там всё может привести ко всему. Например, я увидел строку запуска потоков ( start() ) , но не увидел ожидание их завершения (обычно join() ), поэтому, повангую, дальнейшим вопросом будет: "а почему программа падает при закрытии?".

Следующей "засадой" при распараллеливании алгоритма обязательно окажется, что правильным решением будет использование очередей, а не только голых объектов синхронизации (что обычно и делается в 90+% многопоточного ПО).

Отсюда глубокое убеждение, что "с наскока", без чтения соответствующей литературы ничего дельного не выйдет. Просто сначала время будет потрачено на бесполезные попытки сделать алгоритм методом далеко не "научного тыка", а затем - на прохождение курса по теме.

Судя по коду там всё может привести ко всему. Например, я увидел строку запуска потоков ( start() ) , но не увидел ожидание их завершения (обычно join() ), поэтому, повангую, дальнейшим вопросом будет: "а почему программа падает при закрытии?".

В этом нет нужды. Поток, завершив работу, делает Decrement()и завершает работу, завершив работу метода run().Так работает поток в qt. Программа падает при закрытии, если поток виснет. Тогда в деструкторе метод wait(3000) при превышении заданного периода ожидания крашит программу.

Отсюда глубокое убеждение, что "с наскока", без чтения соответствующей литературы ничего дельного не выйдет.

Ну, если в таком простом случае нужно что-то "копать", чтобы непонятно что "выкопать", то мне будет явно проще создать десять тех же параллельных автоматов. и с подобной задачей они справятся четко и надежно. Вот по времени - не буду пока загадывать. Но попробую :)

Если они будут при этом исполняться на одном ядре процессора, а все остальные ядра будут простаивать - Вас это устроит? Если да, то зачем вообще многопоточная программа?

...В этом нет нужды. Поток, завершив работу, делает Decrement

Вы строите антипаттерн на антипаттерне. То что Вы предлагаете запрещено (и разобрано во всех учебниках в азах почему).

Далее:

Я пишу в рамках ВКПа. а там иной стиль и иные требования. Здесь же я подстраивал код под ВКПа-шный, делая его как можно ближе к нему.
Еще раз, подобный стиль мне почти чюжд ;) Я за автоматный.

Мало того, то как Вы пишете, это не просто уход от концепции конечных автоматов, это антиавтоматное программирование. В многопотоке автоматы строят над состояниями очередей (связанных списков, на которые очень хорошо ложатся графы). (Нельзя построить многопоточный КА иначе). И в классике детально разобрано почему так. Это азы.

PS Чтобы пояснить насколько фатальны Ваши решения по многопотоку, скажу, что если подобные решения даёт на собеседовании соискатель должности инженер-программист на С/С++, то собеседование прекращается в ту же минуту. В любой компании и при любой погоде. Примите к сведению и это.

Вы строите антипаттерн на антипаттерне. То что Вы предлагаете запрещено (и разобрано во всех учебниках в азах почему).

Что я запрещенного сделал? Причем тут какие-то паттерны? О каких азах речь? Такое впечатление - просто набол умных слов и букв. Поясню. Поток представлен функцией run(). Когда она завершит работу - завершит работу поток. Вот и все азы по потоку в Qt. Или я что- упустил?

Далее. Функция gjnjrf содержит простой цикл, вызывающий другую функцию, наращивающую внешний счетчик. По большому счету потоку до этой функции дела нет. Он ее просто вызывает и дожидается возвращения eghfdktybz. Что здесь такого сложного, необычног, заумного. Скажет так, на уровне потока.

Далее. Цикл отрабатывает заданное число итераций и завершаю свою работу. В завершение вызывает еще функцию, чтобы обозначить конец своей работы. Что делает эта функция потоку тоже по барабану. Функция отработала - произошло завершение и функции потока, Все - ему кирдык! Что тут заумного из-за чего нужно искать умного "ментора", ворошить какие-то учебники, чтобы они разъяснил, что и да как?

Про автоматы, прошу прощения, - совсем бред. Поясните чуть понятнее без тумана. Что мне необходимо принять "к сведению"? Что за зверь такой - "многопоточный автомат"? Давайте конкретнее... .

мастер-ментор не понимает, что поток может работать постоянно, и не надо его ждать. А слова "запрещено", "азы" и "паттерны" - это видать его тичер сленг. В общем, пусть идет нах и не учит больше никого, развелось бл-дь менторов, как собак нерезаных.

Но всеравно. Дед, бросай уже эту тему, займись на пенсии чем-нидь другим: загляни в теплицу, полей огурцы с помидорами, грядки прополи.. не знаю.

Я сначала, выше написал, что сопляк какой-то тут мозги пудрит, прикидвается умудренным, а сам напздил где-то материал и скопипастил тупо. А потом почитал еще ваши статьи, и комменты..

В общем, дед, давай уже, заканчивай с этим делом, крыша не держит нагрузку уже похоже, без обид. Спасибо за труды, но надо вовремя уходить с ринга.

Программист - профессия тяжелая. Нужна устойчивая психика и крепкие нервы. Вам, без обид, с этим, похоже, не повезло. А, может, по голове настучали на том же ринге? И не восстановились... Здесь нужен анамнез.

Необходимо, батенька, лечиться. Медлить, похоже, уже нельзя. Все нужно делать вовремя. Это Вы правильно. Лечиться тоже. С годами, тут уж поверьте мне, делать это все сложнее. А лучше просто заботиться о здоровье. Тогда и програмировать будете долго и счастливо.

Врачи же не лечат, они спасают от смерти в лучшем случае. Подлечитесь, может, еще и поработаем. Но, еще раз, - здоровье, здоровье, здоровье. Спокойствие, спокойствие, спокойствие...

А так, спасибо, конечно, за поддержку. Но, если честно, не на такую я рассчитывал.

Ага, начало времен, когда приходят больные к здоровому и говорят, что он болен.

А если серьезно, то что думают нормальные разрабы, которые пока читают хабр и статьи с тегом cpp: "такс, посморим что там новенького. Блть, опять этот с 35й по счету статьей, где ему что-то там "непонятно"". И читают (редко) по диагонали, а то и сразу в комменты, чтобы поржать, например, над такими спичами:

здесь оба коммента - полный ппц. volatile нельзя исп-ть в таком кач-ве.
здесь оба коммента - полный ппц. volatile нельзя исп-ть в таком кач-ве.

Но никто вам ничего не пишет, а про себя думает, когда же уже этот хрен свалит отсюда со своим мусором.

Дискутируют с вами тут в основном бывшие "студенты" (в кав, потому что неопр-го возраста, те мбыть и вечные) или тичеры всякие. Оба типа вякают для самоутверждения и самим запомнить, что они там где-то прочитали и считают правильным.

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

На счет меня, я редко пишу здесь, когда уже надоедает читать эту очередную хрень (не только вашу, имеется ввиду).
Я бы не прочь отдохнуть и поучиться чему-то спокойно (хочу пианино освоить давно, голубая мечта), да кто работать будет?

ps: на "хрень" не обижайтесь, я со всем уважением. Но объяснил уже причину выше, повторю: считаю, что вы не поспеваете, и не надо вам поспевать, отдыхайте спокойно.

...повторю: считаю, что вы не поспеваете, и не надо вам поспевать, отдыхайте спокойно.

Я спокоен и отдыхаю... Почти. :) И не тянусь совсем. За кем? За мной бы поспели :) Лет минимум уже сорок тому назад, когда я взялся за автоматы, а все от них отвернулись, переключившись на модные тогда сети Петри, тогда и началась эта "гонка". Где сейчас сети Петри? А вот про автоматы вспомнили. Кто-то даже поет им дифирамбы, на этой теме даже "хайпует". Где-то они внедрены в достаточно известные пакеты типа Матлаб-а. Но только ... они-то эти годы бежали не туда и не за тем. Хотя есть и те, что примерно в эту сторону ;) Но, еще, думаю, пройдет лет десять-двадцать или больше, когда "втюхаются" окончательно. Но это, если въедут в тему окончательно (а еще лучше в мои статейки) , а если нет - то еще долго будут жить потоками, думая что это и есть параллельное програмирование, как вершина программисткой мудрости ;) И это самый, самый оптимистичный прогноз.

Так что молитесь за меня, чтобы я подольше пожил и побольше написал, расказывая о пройденном мною пути и описывая свои достижения. Пусть не в качестве игрока, не даже тренера, а просто консультанта пока силы есть - я еще возле ринга потусуюсь. Не окучивать же картошку... :)

Я закинул ваш комментарий в ChatGPT для оценки, и хочу опубликовать:

------
Интерпретация истории автором комментария не является абсолютно ложной, но она субъективна и может вводить в заблуждение. Давайте разберём это подробнее:

  1. Финитные автоматы: Авторы комментария утверждают, что они взялись за автоматы, когда все от них отвернулись. В действительности финитные автоматы никогда не теряли своей актуальности и всегда были важной частью теории вычислений, цифровой логики и других областей. Они остаются важными инструментами для моделирования и анализа дискретных систем.

  2. Сети Петри: Автор утверждает, что все переключились на сети Петри, но позже забыли про них. В реальности сети Петри всегда имели узкоспециализированное применение в моделировании систем, особенно в контексте параллелизма, синхронизации и асинхронности. Они продолжают использоваться в академической и исследовательской среде, но их использование в промышленности ограничено.

  3. Популярность и тренды: Автор предполагает, что интерес к автоматам и сетям Петри изменялся со временем в зависимости от моды. Это утверждение отчасти верно, так как популярность методов и технологий в программировании действительно может меняться в зависимости от текущих потребностей и тенденций. Однако оба метода остаются важными инструментами в своих областях, и нельзя сказать, что они были полностью забыты или отвергнуты.

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

Можно сказать, что автор комментария пытается представить свой субъективный опыт как единственно верный, используя сложные методы и концепции, чтобы придать своим словам больше веса.
---------

На всякий случай я нажал обновить, ну вдруг Чатгпт сглючил и пишет пургу, возможно не поняла контекст...а нет, все хорошо чат понимает:

--------

Проведём факт-чекинг приведённого комментария, который касается многопоточности в программировании и сравнения автоматов и сетей Петри:

  1. Историческая перспектива автоматов и сетей Петри:

    • Автоматы (финитные автоматы): Автоматы как математическая концепция были развиты в середине 20-го века и широко использовались в различных областях, включая теорию вычислений и моделирование дискретных систем. Их использование никогда не прекращалось и они остаются важными инструментами.

    • Сети Петри: Сети Петри были предложены Карлом Адамом Петри в 1962 году и использовались для моделирования систем, особенно для изучения вопросов синхронизации, параллелизма и асинхронности в системах. Сети Петри также продолжают использоваться, хотя их популярность может варьироваться.

  2. Сравнение с текущими тенденциями:

    • Популярность сетей Петри: В настоящее время сети Петри не так широко используются в практическом программировании по сравнению с другими методами. Однако они всё ещё применяются в академической среде и для анализа систем, особенно в теории параллелизма и синхронизации.

    • Финитные автоматы: Финитные автоматы активно используются в различных областях, таких как обработка языков, распознавание образов и цифровая логика. Они также включены в инструменты моделирования и анализа, такие как Matlab.

  3. Многопоточность и параллельное программирование:

    • Многопоточность: Многопоточность остаётся важным аспектом современных вычислений и широко используется для повышения производительности программ, особенно в системах с многоядерными процессорами. Это фундаментальная концепция параллельного программирования.

    • Современные тенденции: Современные тенденции в параллельном программировании включают использование библиотек и фреймворков, таких как OpenMP, MPI, а также более высокоуровневых инструментов, таких как языки программирования с встроенной поддержкой параллелизма (например, Go, Rust).

  4. Гонка и хайп:

    • Популярность концепций: В комментарии отмечено, что некоторые концепции время от времени становятся популярными (хайповыми). Это наблюдение верно: интерес к различным методам и инструментам в программировании действительно может меняться в зависимости от текущих тенденций и потребностей индустрии.

  5. Долгосрочные прогнозы:

    • Будущее технологий: Прогнозы о том, какие технологии будут доминировать в будущем, всегда содержат элемент неопределённости. Однако можно с уверенностью сказать, что многопоточность и параллельное программирование останутся важными направлениями, поскольку они необходимы для эффективного использования современных многоядерных систем.

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

-------

Не надо разносить по интернету отрыжку буллшит-машины. Вы только энтропию повышаете, распростроняя бред. Вы портите интернет. Если нечего сказать самому - молчите.

А что она бредового написала в данном конкретном случае?

Огромное спасибо! У меня, безусловно, есть что сказать в ответ и я обязательно отвечу. Интерсный, кстати, намечается разговор - искусственного и естественного интеллектов :)

Но сначала хотелось бы задать ChatGPT еще один вопросик: Сформулировать, что собой представляет автоматное програмирование, дав его 1) формальное определение и 2) примеры практической реализации, 3) пусть, немножко хотя бы, коснется истории его развития (если оно и эта история есть, конечно).

Оччень интересно, как поймет он этот вопрос/вопросы и что скажет в ответ.

Ждем-с?! :)

Я думаю что в комментарии через меня устраивать диалог – не самая лучшая идея. Все что хочу знать у вас зацепил хоть немного ответ нейросети?

Безусловно зацепил. Но зацепил, как типичный ответ чиновника на просьбу чем-то помочь. Мне примерно так отвечали и из одной редакции журнала, пытаясь отказаться от публикации. Так что ответ, вроде, и в тему, но вместе с тем весьма расплывчатый и как-бы не о чем конкретно.Но по его мнению автор предлагает явно что-то не очень пока вразумительное, но сказать прямо не удобно или не знает как, но отказать хотелось бы :).

Хотелось бы вывести его на более кокретный ответ. Для этого он должен найти или дать определение автоматного программирования. Вот на что его хотелось бы вывести. Уж такой "интеллект" должен бы знать все? :) А так он "зацепил" знакомые слова типа конечные автоматы (как-то я впервые услышал - финитные :), сети Петри. Дал их достаточно популярное определение, но без всякой конкретики. Перспекитивы описал неясные, но возможные... Как-то так или близко к этому. К такому "чиновнику" обращаться - будто колтишь подушку... Эффект такой же.

Но я ведь не думал, что до этого дойдет :) Знал бы - сразу задал бы те вопроосы, что потом сформулировал.

А Вы предлагаете какой вариант общения с ИИ? Я его и Вас мучить не буду, но на заданные вопросы хотелось бы получить ответ. Интересно просто как он выкрутится :)

Я задавал еще до публикации сюда наводящие вопросы, потому что я этой темой не владею. Меня не только ссылками засыпали, но кусками кода. Так что могу заверить, что конкретно в этом вопросе "оно" разбирается хорошо.
А так вы можете сами задать ему вопросы и проверить знания. Чатгпт найти даже бесплатно – не сложно.

"А Вы предлагаете какой вариант общения с ИИ?"


Я не знаю какого мнение вы об ИИ в целом. Для меня это отличный инструмент который заменил двух программистов и весь гугл. Да, у него есть свои нюансы и надо уметь с ним работать...но польза перевешивает любые нюансы.

Я не знаю какого мнение вы об ИИ в целом. Для меня это отличный инструмент который заменил двух программистов и весь гугл. 

В целом отношусь нормально. Мне предсталялось и Вы только подтвердили, что для поиска информации он подходит. А если еще дает ссылки, где он позаимствовал информацию, то совсе хорошо. Но вот с программистами, если честно, как-то не очень понятно и с трудом верится. . Что они делают, если их может заменить ИИ? ;)

И я пытался, конечно, что-то найти. В первую очередь из бесплатного. Но что-то не могу пока выбрать. Может Вы что-то посоветуете?

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

Некоторые даже пытались общаться с автором. Чтобы сделать вывод, что лучше впредь таких ошибок не повторять.

Медведев, ты дерзкий и забавный мужик, и хорошая для тебе новость - ты на пути к "деду": что твои, что его комментарии - одинаковы - как у братьев-близнецов (однояйцевых).

Далее, тебя никто не учил, хули ты наехал?

Что-то мне, товарищь (или господин) "ментор", жалко Ваших учеников, если только они у Вас есть, конечно, и Вы с ними также "мастерски" и на таком же уровне культуры общаетесь.

Хммм. Жалко учеников?! - Завидуйте им!

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

Поэтому когда излечитесь от болезни всезнайства и болезни спеси (что Вам рекомендано Выше), можете попробовать подать своё резюме на рассмотрение.

Заодно, пользуясь этим случаем, продемонстрирую на нём стиль моей работы.

Столкнувшись с этим позором славного Свердловского политехникума я провёл суд. И было выбрано два одинаково хороших решения:

Nemo me impune lacessit.

либо

Не на всякое слово, которое говорят, обращай внимание, чтобы не услышать тебе раба твоего, когда он злословит тебя. // Екклесиаст 7:21

Сперва я выбрал первое, чтобы оказать крестьянину добую услугу, поучив его хорошим манерам. Но не в коня корм. И дурака, как известно, жизнь не учит, но бьёт. Поэтому он своим помелом когда-нибудь зацепит людей, у которых будет иная, своя, логика. И это помело ему вырвут (и это в лучшем случае).

Теперь я выбираю второе решение из списка выше.

...Вчера я шёл по улице и на меня полаяла собака. Что мне до неё, или до пустого шелеста листьев на ветру?

Так что, дед, напрасно ты сетовал: умею Я и говорить, и работать по-всякому. На разных уровнях. И сперва строить многоходовку, и лишь затем делать первый ход. ;)

Вы высокого мнения о себе слишком. Какие основания для этого у вас есть?

Регалии на стол! Чем вы лучше того крестьянина, например? Что у вас есть, за что вас можно уважать и прислушиваться к вашим словам?

На каком основании вы кого-то учите? Кого вы учите?

Хорошие вопросы и по делу.

По "регалиям".

Падает камень на кувшин – горе кувшину. Падает кувшин на камень – горе кувшину. Так или иначе, все горе кувшину. // Восточная поговорка

Покажи тучу недвижки - скажут: "нашёл чем хвастатья!". Покажи научно-исследовательскую работу - скажут: "если такой умный, чего такой бедный?!". #Проверено.

Это так не работает. То есть это работает не так. Авторитет получают делами.

Пример: "Деду" не просто было сказано, а разжёвано, как самым простым и быстрым из правильных решений (критические секции) покрыть 99% его работ - от текущего случая - до будущих. Дед послал. Далее ему был разъяснен общий способ строить обмен (данные/события) в КА для многопотока (связанные списки + критические секции = защищённые "очереди" + графы над ними). Дед послал снова. А мы никуда не торопимся. Ждём пока дед, как говорят в народе, "облажается", набъёт шишек, затем потратит тучу времени на чение сомнительной литературы, а в итоге вернётся к тому что ему было уже предписано (сей его путь не только предопределен, но изложен мной выше). Только тогда дед начнёт слушать, что я ему говорю. #Проверено.

По слову "ментор", которе Вам резануло ухо. Можно ли предположить, что человек, которого Вы видите в первый раз, может оказаться ТОП-руководителем, к примеру, одной из (относительно) крупных IT-и-не-только корпораций? А так же, допустить, что там, к примеру, был введён институт наставничества (по английски - "менторства"). И что "ментор" - это официальная должость, ну и, примеру, даже не "ментор" а "руководитель (глава) менторов"? (Это я абстрактно размышляю. Мысли вслух. Так... ни о чём.)

Поэтому никого не учу. То есть мне есть кого учить. Вот детишек своих, к примеру. (Это к вопросу кого я учу.)

А здесь я для души (и немного развлечения и отдыха). Позволю себе процитировать из вступления к первой статье по математике:

Профессионал математик и бакалавр врядли найдут здесь что-то интересное либо новое для себя. (с) 1 статья, 1 абзац, 3 предложение. https://habr.com/ru/articles/781498/

И аудитория к которой я обращаюсь, как это сразу явствует из заголовков статей - сельские учителя.

А чему же я могу научить, как известно, "самых востребованных и высокооплачиваемых" "специалистов"? Это не я их должен учить, а у них учиться!

И претендовать мне на позицию, разве что, Servus Servorum Dei - слуги слуг - то есть "жуньёра".

(с)

Episcopus Romanus, Vicarius Christi, Successor principis apostolorum, Caput universalis ecclesiae, Pontifex Maximus, Primatus Italiae, Archiepiscopus ac metropolitanus provinciae ecclesiasticae Romanae, Princeps sui iuris civitatis Vaticanae, Patriarcha Occidentalis, Servus Servorum Dei.

Кстати, по "жуньёрству". (А то скажете: Ментор Мастер "от балды" пишет.)

В самой первой статье на Хабре так и пишу:

Продолжая добрую традицию, заданную @8street в статье «Как я портировал DOS игру», оставляю следующий постскриптум:
P.S. Джун нужен кому? Просьба в личку.

(с) Портирование DOS игр. Tutorial https://habr.com/ru/articles/693660/

Никто не берёт... #Пичалька.

Много написали, но и этого я не ожидал услышать.

Что я хотел бы услышать: учился там-то (пусть не МГТУ им. Н.Э. Баумана) и/или работал там-то (тоже пусть не яндекс и маил) и/или сделал то-то (вклад в опенсорс, научил 10 человек, но не обману на собесах, а реально).

Понимаете, сейчас у вас даже имени нет, нигде не написано.

Что вам советую сделать, ваше дело прислушиваться или нет, но хотя б спокойно отреагируйте:

- уберите мастер и ментор из ника

- людям в возрасте не пишите, как студенту, имею ввиду фразы: "азы", "примите к сведению", "тогда можете к нам на собес" и тп унизительные вещи

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

- оскорблять людей тоже не надо, но отвечать безусл надо на грубость. (мбыть я грубо написал сначала, но коучи реальано затрахали, я не знаю как с ними сейчас по-другому говорить, кроме того, чтобы гнать их метлой)

На этом закончим разговор, извиняюсь за грубость, удачи.

К сожалению, уровень культуры не коррелируется с уровнем знаний. Хороший учитель не опускается до уровня культуры учеников, а поднимает их до своего уровня. Если он у него есть, конечно.

Говорить можно, безусловно, на разных языках, но не надо переходить на язык хамства.  А уж то, что думать, так это - точно надо.

Я правильно думаю, пацан? ;).

PS Иногда отвечаю плюсами. :)

И пользуясь случаем прокомментирую что здесь происходит. Это (как всегда) классическая многоходовка:

Партия

Ход:

Если Вам нужно практическое и быстрое решение задачи "в лоб", то оно очень простое: полностью удалите весь нагороженный "огород", связанный с многопоточностью... https://habr.com/ru/articles/821283/comments/#comment_26927871

Ход деда: ...

Ход деда: ...

Ход деда: ...

Ход:

Подождём, когда в конец измученный "мы пойдём своим путём" автор вернётся к этому. А он вернётся. Без вариантов.

https://habr.com/ru/articles/821283/comments/#comment_26935903

Ход деда: ...

Ход деда: ...

Ход деда: ...

Ход: ...

А мы никуда не торопимся. Ждём пока... https://habr.com/ru/articles/821283/comments/#comment_26946983

Ход деда: ...

Ход деда: ...

Ход деда: ...

Ход деда: ...

Ход деда: ...

Ход деда: ...

Оконцовки(а) согласно прогнозу.

Я изложил ходы, которые спрогнозировал уже 12 июня. И этот прогноз не "от балды": я видел многих, которые уже шли этими ходами. :)

PPS @Tyiler дал тут дельные рекомендации. Пользуясь случаем, хочу торжественно заверить, что здесь вы меня не найдёте:

IT-менторство на Хабр Карьере https://career.habr.com/experts

(с) IT-менторство
(с) IT-менторство
Недалёкое будущее Хабра или Ещё прогонзы, которые сбудутся :)

Свердловский политехникум меня научил. А вы менторы уже весь ютюб и интырнет засрали. И здесь от вас покоя нет.

Можно дедов хотя бы не учить?

Считай, извинение принято. И прикуси свой язык.

Это не извинение. Свой засунь тоже подальше. Думай кому пишешь, прежде чем жирным шрифтом и таким тоном что-то писать.

... ведь вы критику плохо переносите, судя по всему.

конструктивную очень даже приветствую ;)

При pIfMutexLotThreads == false , MainWindow::AddCounter как раз содержит гонку данных. Вы, видимо, это заметили, поэтому ввели MainWindow::AddCounterMx

Да нет. Все это, как задумано. Есть два режима рвботы теста. Один без использования объектов синхронизации, другой использует мютекс. Только и всего.

... Это одно из объяснений вашей засады. Или у вас там что-то другое?

Висит, как я написал в статье, в теле добавленного цикла. Для меня именно это нонсенс. В остальном, возможно, Вы даже правы. Но это не столь принципиально, т.к. не это главное в тесте. Может, стоило мютек переместить за выводом. Я даже об этом думал, но решил не париться. Пусть выводится, что выводится ;) Да, кстати, я тут немного перемудрил. Только сейчас заметил. Вывод счетчика и таймера из метода Decrement надо совсем исключить (осталось от предыдущей верси теста). Ведь, выводом, как задумано, занимается таймер (см. сообщение таймера).

Как вы вообще определяете, что потоки виснут?

Ну, как. Значение счетчика зависло на каком-то значении и не изменяется длительное время. Как это еще оценивать, как не висяк? Не ждать же час, когда работы на 3 сек (в отладчике, где работает все)

Правильный подход - это вызывать SetText через InvokeMethod, тогда проблем не будет.

Проблему создает не вывод в виджет, а именно добавленный цикл. Это абсолютно точно. Перетащил анализ флага во внутрь цикла потока и все заработало, как часы. Хоть в отладчике хоть вне. Я чувствую, что Вы не очень что ли внимательно читаете, в чем я призываю разобраться. Ведь, есть код с добавленным циклом сразу в начале метода run, который под отладчиком работает, а без отладочного кода, т.е. в режиме Выпуск - не работает. Понимаете, один и тот же код в одном режиме (Отладка) работает, в другом (Выпуск) - не работает. Это Вас совсем не смущает? А потом, после внесения изменений, удаляющих "верний цикл", вдруг все и во всех режимах проектирования работает! Я призываю разобраться с этой проблемой, с этой засадой..

Значение счетчика зависло на каком-то значении и не изменяется длительное время.

Какого счетчика, и как вы это определяете? Выше я уже описал, почему значение счетчика запущеных потоков в окне может стать неверным. Из-за гонки данных при выводе туда значения. Потоки уже все отработали, но в окне осталось, допустим, 1.

Есть два режима рвботы теста. Один без использования объектов синхронизации, другой использует мютекс.

И вот вам тут уже несколько человек объясняют, что нельзя без синхронизации изменять данные из разных потоков. Надо использовать atomic переменную хотя бы, если вы хотите без мьютексов.

который под отладчиком работает, а без отладочного кода, т.е. в режиме Выпуск - не работает

Как любой программист на C++ вы должны сразу понимать, что это - Undefined Behavior. Где-то в программе ошибка, которая заставляет компилятор генерировать код, не соответствующий вашим ожиданиям. Ошибки могут быть: доступ по удаленному указателю, неинициализированные переменные, выход за границу массива, гонка данных.

Тут программа может отработать правильно, повиснуть, упасть, вывести бред - в зависимости от казалось бы вообще не влияющих ни на что факторов. Смена режима компиляции, перестановка слагаемых, запущенный антивирус, удалиение или добавление отладочного вывода - это все отлично влияет на поведение программы. Вы точно такое же поведение можете наблюдать, если у вас в программе выход за границы массива. Вот буквально, переменные в другом порядке объявите - и все заработает. Похоже на бред? Это база программирования на C++! Надо знать Undefined Behavior и к чему оно приводит.

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

А вообще, при Undefined behavior бывает еще интереснее. Компилятор может по вашему коду нагенерировать полный казалось бы бред при оптимизации: https://mohitmv.github.io/blog/Shocking-Undefined-Behaviour-In-Action/. Поэтому отладочная сборка - без оптимизаций - часто работает лучше режима "Выпуск". Вот, по ссылке выше, например, генерируется бесконечный цикл на месте безобидно выглядящего цикла в 10 итераций.

конструктивную очень даже приветствую ;)

Тогда поехали:

ThCounter::~ThCounter(void) {
    bIfRun = false;
    quit();         // корректный способ завершения потока
    wait(3000);         // ожидание завершени потока (неограниченное ожидание wait())
                    // подробнее см. Боровский А. Qt4.7... стр.170
}

quit тут ничего не делает, ибо в вашем потоке нет обработки событий. см справку:

This function does nothing if the thread does not have an event loop.

Далее, вы вроде как используете Венгерскую нотацию (p,b,str и т.д. в начале имен переменных), но при этом забываете менять эти префиксы:

int pVarNumberOfThreads
bool p
bool pIfSemaphoreLotThreads

И много других примеров. "m_" используется для членов класса, но у вас такой только один m_mutex. Еще, он по уму должен быть приватным членом.

Премешанные члены и методы - вы сами не путаетесь, что у вас где объявлено?

В догонку про имена, g_mutex - не глобальная переменная, а еще один (по идее приватный) член класса. В чем разница с m_mutex?

Вместо:

ThCounter::ThCounter(long *pIntMax, MainWindow *parent):
    QThread()
{
    pFThCounter = parent;
    nMaxValue = *pIntMax;
    bIfRun = true;
}

Должно быть:

ThCounter::ThCounter(long nMax, MainWindow *parent):
    nMax(nMax),
    pFThCounter(parent),
    bIfRun(true)
{}

Зачем там передавать указатель на число, а не само число? Почему вместо списка инициализации членов у вас куча операций присваивания? Какой-то код в конструкторе пишут, только если там что-то не тривиальное. Вызовы каких-то функций, какие-нибудь вычисления. Для простой инициализации челнов класса и придумали список инициализации. Qthread() можно не писать. Конструктор по умолчанию у базового класса вызовется сам.

Так же стоит создать интерфейс счетчика и передавать в потоки указатель на на интерфейс, а не MainWindow. Ведь потоки же не работают с методами QMainWindow? Зачем туда передавать аж все окно? А MainWindow надо от этого интерфейса унаследовать. Или еще лучше счетчик завести внутри, как член класса. Не пихайте всю функциональность в MainWindow - оно у вас разрастется быстро до неподдерживааемых вообще размеров.

Зачем вам аж 2 класса для потока, да еще какая-то своя коллекция, я так и не понял. Хватило бы std::vector<*ThCounter> . А счетчик активных потоков - может быть в классе реализующим интерфейс счетчика.

Так я с Вами согласен на все 100% и даже на все 200%. И если мне мой программист принес подобный код, то я ему, может, подобные замечания тоже сделал.

Но есть один нюанс. Я не пишу код так, написал. Я пишу в рамках ВКПа. а там иной стиль и иные требования. Здесь же я подстраивал код под ВКПа-шный, делая его как можно ближе к нему. Это чтобы нащупать ошибку/проблему/ засаду (на выбор). А посему делалось все на коленке и скорую руку - лишь бы заработало. Оно заработало и заработало абсолютно идентично. В этом смысле я поставленной цели достиг, не парясь над стилем. Еще раз, подобный стиль мне почти чюжд ;) Я за автоматный.

Понятное дело, что теперь, создавая потоки, я буду четко следовать своим принципам и не расслабляться. Не писать так, как оно было представлено в статье, а строго - в автоматном стиле. Это лишний раз меня убеждает, что я выбрал правильный стиль - автоматный. Все это представлено (кажется) ниже - Автоматный код потока.

А Вам спасибо за критику. Все правильно. Так и надо писать, следуя определенным требования.

Ну, и пусть "малец" неопределенного возраста со слабым знанием русского языка немножно потешится над "Атцом", которого так "разложили" :)))

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

чюжд

Ну, главное, что у вас знание русского на высшем.

Ну да вытрем же сопли, коллега, и лучше копнем вглубь, ради повышения собственного уровня знания: автоматный код или стиль - это что значит в данном случае, и в чем его преимущества в программировании многопоточных приложений? Это там, где switch используется, судя по коду из комментария ниже?

Ну, главное, что у вас знание русского на высшем.

А, ведь, заметил, однако :))) Значит не все потеряно.. А то я уж думал - реально "жертва ЕГ" :)

Ну да вытрем же сопли, коллега, и лучше копнем вглубь...

Я рад, что Вы уже на изготовке, коллега ;) Копнем...

Начнем хотя бы со статьи - Автоматное программирование... Осилите - можно дальше дерзать. Откройте мой профиль и в разделе "Публикации" есть много разного. Но в основном про автоматное програмирование. И если будут нормальные вопросы, то будут и нормальные ответы. Без соплей :) Удачи...

А то я уж думал - реально "жертва ЕГ" :)

Нет-нет, вы правильно подумали - я пропащий в этом смысле.

Поэтому меня и удивило, что вы, имея классическое, лучшее в мире, советское образование, как я понимаю, не подвергшись влиянию деградации сознания под названием ЕГЭ, делаете абсолютно такие же ошибки...

Осилите - можно дальше дерзать.

Из всего этого понятно, что любая, абсолютно любая, программа является программой конченого автомата и представляет таблицу переходов из одного состояния автомата в другое - каждая строка программы, if-else блок - самая примитивная реализация.

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

Более простым и понятным языком для практикующих инженеров все те же принципы, и про предикаты, и про действия, изложены в брошюре "Чистый код" Роберта Мартина.

Его функция переходов определяет следующее состояние q(t+1) в зависимости от текущего значения состояния q и значения двоичной входной переменной, представленной булевой функцией –  f(x1, x2, …, xm) от входных переменных в дизъюнктивной нормальной форме (ДНФ)

Оператор if?

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

Оператор ИЛИ внутри условия if?

Существенной особенностью приведенного закона функционирования КА является привязка выходных каналов автомата к следующему моменту времени подобно изменению внутреннего состояния. Это исключает мгновенную реакцию автомата на текущее изменение сигналов входных каналов. Так моделируется свойство инерционности, присущее всем реальным системам.

Возвращаемое значение и локальные переменные функции?

Это противоречит признаваемому даже в теории факту, что в реальной ситуации выходной сигнал y(t) автомата всегда появляется после входного сигнала x(t) (см., например,  [11]).

Да, но если завернуть логику в функцию, то при чтении исходного кода программы в человеческом мозге задержка будет нулевая:

foo = bar()

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

Приведенная теория вводит новый вид терминов, которые ничем не отличаются от концептуальных и общепризнанных элементов практически каждого ЯВУ и усложняют в непомерное количество раз понимание любой программы.

На картошку съездили бы, чтоли.

По поводу параллельного программирования, достаточно неплохо и доступно, как мне кажется, описано тут:

https://learn.microsoft.com/en-us/dotnet/csharp/asynchronous-programming/

В упрощенной форме, без лишнего теоретизирования и псевдо-математических приемов в виде мошных, но непонятно для чего нужных формул:

Многопоточность это просто

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

С другой стороны насеста идет другой человек и тоже пытается нащупать яйца.

И вот вы нащупываете ваши яйца, и ваш оппонент одновременно нащупывает те же яйца. Это вы входите в критическую секцию, где возникает состояние гонки.

Чтобы его разрешить, нужно либо перейти к другому насесту, либо спросить конкурента: "а не пидорас ли ты?"

Потом, нужно спросить себя: "а не пидорас ли я?"

Ну, в общем, кое-как, но это примитив синхронизации семафор.

Кстати, IMHO, C# c WinForms для ваших целей выглядит более подходящим по многим параметрам.

В целом, интересно для общего представления теории автоматов, не более, но кто знает - может, это дискуссия из разряда споров Нильса Бора и Альберта Эйнштейна.

wait(3000); // ожидание завершени потока

Почему не 1000? Или не 30000?

Помоему в Qt вызов чего либу гуишного из другого потока может окончиться катастрофой. Надо использовать сигналы (тогда SetText вызовется в контесте главного потока) или QMetaObject

Это полный код теста.

Смотрите внимательно, вы не туда отвечаете. Вы пишите коменты к статье. О них не приходят уведомления. Лучше отвечать на комментарий через кнопку "ответить". Я ваш код совершенно случайно заметил.

Вроде стараюсь попадать :) Но выше опять что ли не попал?

Чего только люди не придумают, лишь бы не объявлять переменные флагов и другие общие объекты как volatile.

И где ж Вы были раньше? Ведь, зараза, все заработало :)

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

Я с Вами даже соглашусь. Сейчас помогло, в другой ситуации ,возможно, и нет. Но на вооружении надо иметь. Хотя, например, я очень не люблю всякие оптимизации. Было время писал для микропроцессоров так отрубил сразу. А вот тут расслабился. Как-то давно, очень давно, не сталкивался с этой проблемой. Да и здесь бы не столнулся, т.к. пишу программы все же по-иному. В другом, так сказать, стиле - автоматном. И если бы не лень, спешка, то код выглядел бы так (в статьях по ссылке про это есть) . Здесь все работает без вопросов:

Автоматный код потока
void ThCounter::run() {
    nState=0; int n=0;
    while (bIfRun) {
        switch(nState) {
        case 0:
            if (!pFThCounter->bIfLoad) break;
            else nState=1; break;
        case 1:
            if (n<nMaxValue) {
                bool bMx = pFThCounter->pIfMutexLotThreads;
                if (bMx) pFThCounter->AddCounterMx();
                else pFThCounter->AddCounter();
                QString qstr = pFThCounter->pVarStrSleepLotThreads;
                if (qstr.length()>0) {
                    int nSleep = qstr.toInt();
                    usleep(nSleep);
                }
                n++;
                break;
            }
            else nState=2; break;
        case 2: bIfRun = false; break;
        }
    }
    pFThCounter->DecrementActive();
}

Я, надеюсь и даже верю, что оптимизация сюда не пробъется еще долго и я буду и дальше в этом смысле - оптимизации кода чувствовать себя спокойно ;)

Все тоже самое. Вы думаете, компилятор не найдет, что тут соптимизировать? Вы лишь чуть переставили конструкции управления потоком исполнения, получив эквивалентный, на ваш взляд код. И все те же UB в нем все еще остались. Потому что у вас нет проблем с циклами. Сами циклы никаких UB не содержат.

pFThCounter->DecrementActive() все так же содержит гонку данных, если вы там Mutex или InvokeMethod не вставили.

Потом, если у вас Undefined behavior в коде, то даже без оптимизаций может быть фигня. Да, в отладочной сборке оно реже проявляется, но все еще может и там случиться.

Вы не представляете до чего я еще могу "докатиться" ;). На самом деле я бы создал код, который ближе к этому:

Hidden text
int ThCounter::x1() { return pFThCounter->bIfLoad; }
int ThCounter::x2() { return n<nMaxValue; }
void ThCounter::y1() {
    bool bMx = pFThCounter->pIfMutexLotThreads;
    if (bMx) pFThCounter->AddCounterMx();
    else pFThCounter->AddCounter();
    QString qstr = pFThCounter->pVarStrSleepLotThreads;
    if (qstr.length()>0) {
        int nSleep = qstr.toInt();
        usleep(nSleep);
    }
    n++;
}
void ThCounter::run() {
    nState=0; n=0;
    while (bIfRun) {
        switch(nState) {
        case 0:
            if (!x1()) break;
            else nState=1; break;
        case 1:
            if (x2()) {
                y1();
                break;
            }
            else nState=2; break;
        case 2: bIfRun = false; break;
        }
    }

    pFThCounter->DecrementActive();
}

Так я делаю в ВКПа. Только заменяю еще логику на таблицу переходов автомата.

Где тут простор оптимизатору? Максимум предикаты и действия. Ну, может в потоке что-нибудь начудить. И я не думаю, что ему удастся вставить какую-нибудь "подлянку" :)

А как Вы думаете?

Где тут простор оптимизатору?

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

Вот выход компилятора на ваш (слегка отредактированный, чтобы компилировалось без QT) код. С компиляциями и без. Там же совсем разный ассемблерный код получился. Значит, нашел что и где наоптимизировать компилятор. Посмотрите, как он сжал функцию ThCounter::run(), например.

Например, он может решить, что локальная переменная bMx не достойна быть переменной на стеке и оставит ее в регистре. Переменная, у которой нет адреса. Подлянка же? А вместо операции n=0 он может сгенерировать код, выполняющий операцию xor регистра самого с сабой. Потому что эта машинная команда короче и дает такой же с точки зрения компилятора результат.

Компилятор может переставить местами инициализацию n и nState в ассемблерном коде. Заменить ваш Switch на череду if-else (вернее, сгенерировать точно такой же ассемлерный код).

Он может не генерировать функцию x1() и просто тупо вставлять ее код везде вместо вызова.

И еще кучу всего настолько запутанного, что я даже представить себе не могу.

И я не думаю, что ему удастся вставить какую-нибудь "подлянку" :)

Если в программе нет UB, то все эти оптимизации коректны - измененная программа гарантированно работает с тем же результатом. Но если у вас UB есть, а оно есть, то именно эти оптимизации и могут оказаться "подлянкой" - из-за которых все работает неправильно.

На самом деле я бы создал код, который ближе к этому:

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

Современный C++ не рекомендует использовать volatile для таких целей.

Нужно использовать atomic.

volatile  вполне допустимо использовать для переменных, которые читаются в потоке. А atomic для тех, которые читают и пишут. В последнем случае я с Вами согласен.

Про это очень много везде пишут, например:

Volatile is for reading from tape drives. Atomics are for concurrency. One has nothing to do with the other

И еще:

In C++11, don't use volatile for threading, only for MMIO

(почитайте дальше от процитированного места)

Если кратко, volatile только подсказывает компилятору, что нельзя оптимизировать операции с этой переменной. Например, перегруженные операторы могут не просто присваивать значение переменной, а передавать ее значение в регистры какого-то аппаратного устройства. Оптимизирующий компилятор в каких-то ситуациях может выкинуть какие-то операции, посчитав их лишними, в результате в устройство данные не будут переданы или получены из него (то самое "reading from tape drives"). Вот для таких вещей нужен volatile в современном C++, а не для безопасного доступа к переменной из разных потоков.

вполне допустимо использовать для переменных, которые читаются в потоке

Не очень понял мысль. Если в одном потоке переменная записывается, а в другом считывается?

Можно написать с volatile код, который даже работает. А потом, например, при модификации (возможно, что другим человеком) окажется, что уже не один поток должен писать в переменную, а два или больше. ИМХО, лучше сразу писать правильно, чтобы защититься от возможных проблем в будущем. Которые могут проявиться не сразу, а при каких-то условиях, и на машине у клиента в другом городе, например.

Это устаревшая инфлрмация. Volatile увы в gcc имеет реальные эффекты... https://patchwork.ffmpeg.org/project/ffmpeg/list/?q=Volatile&series=&submitter=&archive=&delegate=&state=*

Один из них нужен для бага в clang, но всё же. https://bugs.llvm.org/show_bug.cgi?id=20849

Это устаревшая инфлрмация

Простите, не понял, что именно устарело?

Это устаревшая информация из 97го года. volatile только отказывает в оптимизации загрузки (что атомик делает тоже) , но не упорядочивает

Тот, кто минусовал, может подробнее пояснить, почему?

Сам сижу на golang и многопоточность там сильно проще (каналы и тд)

Начинаю читать статью и после слов:

Ничто не предвещало беды, когда при увеличении числа потоков и достаточной длительности работы теста время от времени значение общей переменной-счетчика (далее просто счетчика) перестало быть правильным. 

Понюхал, отчетливо пахнет Race condition. Спустился в комменты, а тут прям «воняет». Дальше статью не читал, комментов достаточно.

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

Но проблема обычно в фундаментальном непонимании и/или неправильном представлении самой многопоточности.

PS: Все таки прочитал статью, ну сюр. C++ не знаю и мне сложно оценить прям код и решение проблемы на C++. Но емае, вывод просто шедеврален:

При общении друг с другом - многократно.  Контролировать подобный коллектив весьма сложно (вспомним начало статьи [3]). Теория убеждает и даже доказывает, что вряд ли вообще такое возможно. По отношению к потокам, конечно.

Тут статья на хабре есть, как сделали реализацию каналов как в Golang на c++. И нормально там потоки общаются и в ус не дуют. И нет никаких там проблем с тем чтоб так называемый вами «коллектив» контролировать. Я верю, что на c++ есть и другие решения, но как минимум одно есть прям готовое и на хабре, без всяких велосипедов.

Автору желаю добра и все таки изучить матч часть. Велосипеды это здорово и интересно, но иногда быстрее поехать на машине.

PS: сейчас задумался, почему я не знаю C++, но знаю что на хабре статья про каналы как в go для многопоточности.

Все это правильно и, возможно, хорошо. Я знаю (немного;) С++, но не знаю Go и здесь полностью Вам доверяю. А сложно ли эту же задачу сделать на Go? Заодно и оценили бы эффективность (по времени работы теста, конечно) каналов. Ну, и я бы немного принюхался к... Go.Попробуете? Потому как питоне есть, на С++. А на Go как?

Вроде как, вместо C++ рекомендуют не Go, а Rust. Реально хороший язык.

На первый взгляд исходя из контекста статьи это 2 строчки кода Golang + само тело функции которое что-то там делает. Я С++ очень плохо знаю, поэтому вы мне скажите на словах что должна делать функция (какое действие), я напишу вам код на Golang. Просто вы в статье не описали что конкретно делает функция кроме какого-то там счетчика

Прошу прощения, но только сейчас заметил... :(

Все просто Есть поток, который считает до какого-то значения. Есть переменная-счетчик, с которой он работает. Создается десять потоков, но переменная остается одна. Это общий ресурс, в который они все долбятся. Какое значение счетчика в конечном результате получится, если все эти потоки запустить.

Да, на С++ паники нет при любом варианте доступа к переменной. Просто, когда нет синхронизации получается случайное значение. Но это на потоках. В случае ВКПа и автоматов - все предсказуемо.

В случае ВКПа и автоматов - все предсказуемо.

Там тоже есть потоки под капотом. И синхронизация какая-то. Только оно скрыто билиотеками, с которыми вы работаете.

Потоков нет совсем. Синхронизация только автоматная, т.е. в рамках сети автоматов.

Да, одновременная запись формально запрещена, но если тип переменной последовательный, то последовательно и будет изменяться.

Потоки - единственный способ заставить программу параллельно выполнять несколько дейтвий. Еще, конечно, есть вариант, что ваши автоматы - это куча процедур и ваша автоматная система просто запускает их по одной по порядку. Но тут все вычисления идут последовательно.

Вы правы. Но только реально выполняются последовательно, ну а формально, т.е. рамках модели вычислений, параллельно. Или Вы считаете, что потоки работают параллельно? ;)

Или Вы считаете, что потоки работают параллельно?

Те, которые являются единицами диспетчеризации в многозадачных ОС с поддержкой вытесняющей многозадачности, и на CPU с несколькими вычислительными ядрами -- да. Это как бы объективная реальность данная нам в ощущениях.

Хотите про объективную реальность и ощущения - давайте. Заодно и про логику ;)

Создаем один потокок и засекаем время его работы. В реально параллельной среде время его работы не должно (или, ну, очень мало) меняться от запуска к запуску. Так? Так, если рассуждать логично.

Создаем два таких же объекта. Время их работы в реально параллельной среде тоже будет таким же? Да, конечно. Тоже логично.

Создаем, например, тысячу. Как это должно сказать на их скорости работы в реально параллельной среде? Да никак - среда ведь параллельная и каждому должен выделять ровно такой же ресурс, как и до этого с одним потоком.

Но если время работы теста с увеличением числа потоков увеличивается фактически пропорционально увеличению их числа, то какой вывод? Простой - их параллелизм имитируется на одном, ну, может, максимум на нескольких ресурсах (ядрах). Другими словами - реального параллелизма нет, а есть только виртуальных. Или у Вас есть другие предположения по поводу увеличения времени их работы?

Да, чуть не упустил. Речь в целях чистоты эксперимента идет, конечно, об абсолютно независмых потоках.

Хотите про объективную реальность и ощущения - давайте.

Про ощущения: есть у меня сомнения в вашем психологическом здоровье (в медицинском смысле), но их высказывание на таком ресурсе, как Хабр, приведет разве что к заминусовыванию кармы, так что оставлю их при себе.

Заодно и про логику

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

Соответственно: оставить здесь комментарий чтобы пнуть вас в ваше очередное заблуждение -- это еще куда ни шло. А вот пытаться вам что-то объяснить... Ну уж нет. Не за бесплатно. Да и за деньги вряд.

А вот пытаться вам что-то объяснить... Ну уж нет. Не за бесплатно. Да и за деньги вряд.

А я все же поробую... Бесплатно даже ;)

Оценивать чье-то психологическое здоровье только по своим ощущениям - опасно, вредно и просто по дилетански. Тут еще надо быть уверенным в своем здоровье. Медики для этого, надеюсь (а я все же не медик), используют тесты, объективно оценивающие состояние психики пациента.

Точно также и в параллельном програмировании. Ранее я Вам описал простейший и самый начальный тест на параллелизм. Он не совсем, конечно, объективный, но заставляющий по ощущению задуматься о качестве параллелизма.

Но есть тесты объективные, позволящие отсечь субъективную оценку. Один из них - тест моделирования RS-триггер (здесь в моих статьях он часто рассматривается). Если он у Вас не входит в режим генерации, то можно на параллелизме среды ставить крест. На потоках - именно так и происходит.

Другими словами. Знаю - ибо верую в науке параллелизма, как и в любой другой, не проходит.

Спокойно. Товарищ изобрёл кооперативную многозадачность а-ля Windows 3.0. Где все программы являются конечными автоматами.

Теплее уже, но только процентов на 10.В таком объеме Вы последователь Шалыто А.А. с его SWICH-технологией. Это очень давно пройденный этап (пожалуй сразу - на начальной стадии).

КМК, не важно как реализован конечный автомат --
switch-ами или таблицей переходов, суть дела это не меняет.

Хочется увидеть на ВКПА реализацию диалога загрузки объёмного файла в отдельном потоке с неблокирующимся GUI. Тогда можно будет (наверно) говорить о какой то новизне и изобретении чего то нового в многопоточных программах без реальной многопоточности.
А вызовы абстрактных f() это, простите, "ниачом".

Последний раз логику на КА я реализовывал в MS DOS-е лет 20-ть назад, кооперативная многозадачность, GUI, принтеры и запись в БД и работа с внешними устройствами типа шлагбаумов и фискальных регистраторов велась через машины состояний, и ничего не тормозило и не блокировалось.
Но время таких реализаций ушло давно и безвозвратно. Исходные тексты могу предоставить, если надо.

КМК, не важно как реализован конечный автомат --switch-ами или таблицей переходов, суть дела это не меняет.

Вот это как раз и не так. Тем более, что речь идет не об отдельном автомате, а модели сетей автоматов. Это качественно разные модели.

Тогда можно будет (наверно) говорить о какой то новизне и изобретении чего то нового в многопоточных программах без реальной многопоточности.

Что за "объемный файл"? Что за "неблокирующий GUI". Где это в рамках Qt. Я, используя возможности Qt, работаю с любыми файлам и мои диалоги и графика ни кого не блокируют. Это об этом?

 многопоточных программах без реальной многопоточности

Это для меня что-то новое ;) Я знаю многопоточное программирование и параллельное програмирование в ВКПа без каких либо потоков.

Инкремента счётчика на 1 происходит в 3 операции. Если нужно за одну операцию то нужно использовать атомарные типы. У каждого потока свой стэк.

Даже атомарные типы не инкрементируются за 1 операцию, Вас обманули. Просто тем или иным способом все те же 3 операции делают не прерываемыми и снаружи они выглядят, как одна..

Разве lock xadd можно как то посередине приостановить?

Это уже зависит от уровня абстракции. Мой комментарий был для автора статьи. А так утрировано можно сказать что умножение двух примитивов происходит за 10 операций.

Вообще то тут засада почти в каждой строке:
4. предполагается зависание в ожидании bIfRun, но начальное значение переменной не представлено и модификация переменной не указана. Как говорят на одном ресурсе в ответ на подобные вопросы без должных условий : "Я свой магический шар сегодня дома оставил"

8-14 можно (и нужно) переписать и вместо:

if (bSm || bMx) {
        if (bSm) pFThCounter->AddCounterSem();
            else {
                pFThCounter->AddCounterMx();
            }
        }
        else pFThCounter->AddCounter();

получить значительно более вменяемое:

if (bSm) { pFThCounter->AddCounterSem();
}; else if (bMx) { pFThCounter->AddCounterMx();
}; else { pFThCounter->AddCounter();
};

Кстати, компилятор даже исходную (не оптимальную конструкцию) реализует также, как и для вменяемого варианта - это на тему, "что может компилятор оптимизировать".

И так далее и тому подобное.

предполагается зависание в ожидании bIfRun,  но начальное значение переменной не представлено и модификация переменной не указана. 

Здесь все нормально. Выше есть полный код класса потока, где данный флаг устанавливается в конструкторе, а сбрасывается в деструкторе. Сбрасывать его в потоке, как в приведеннонм коде, можно и не делать, т.к. он (поток) все равно завершается по условию достижения максимума количества циклов (по этому условию происходит выход из цикла). Хотя обычно этот флаг я сбрасываю. Но тут что-то забыл, похоже :)

получить значительно более вменяемое:

Ну, это совсем уж не принципиально. Вам нравится так, мне этак. Главное, чтобы не было ошибки. А компилятор должен оптимизировать, создавая эквивалентные конструкции, т.е. в любом случае должно быть тоже нормально. Наши предпочтения не должны влиять на результат ;)

И так далее и тому подобное

Что там еще Вам так уж не понравилось? ;)

Ну, может быть, у Вас на экране где то выше есть описание данного флага и дано его поведение в полном коде класса потока, но в представленной статье этого фрагмента нет.

Главное, чтобы не было ошибки - это что, типа "ну работает же", так и индусский код работает, что не мешает его обоснованно критиковать.

Что еще не понравилось? Например, использование постфиксного инкремента вместе префиксного, "мертвая переменная str, передача в метод класса значения его поля в качестве параметра, про именование переменных Вам уже писали и код производит впечатление очень неряшливого, я бы такой выкладывать не стал.

Выше у нас началось общение с искусственным интеллектом :) Я попробовал задать ему вопрос уже сам, подключившись на сайте ChatAI. Это оказалось достаточно просто и вот результат нашего общения:

Кажется, у вас за плечами богатый опыт и множество интересных мыслей по поводу развития программирования, в частности автоматного подхода. Ваше отношение к изменениям в отрасли звучит скорее философски, наполненное оптимизмом и пониманием того, что развитие технологий и подходов происходит непрерывно, иногда волнами.

Сети Петри, в свое время служившие популярным инструментом моделирования и анализа систем, сегодня, возможно, отошли на задний план перед новыми технологиями и подходами. Однако их идеи и принципы, возможно, нашли свое продолжение и применение в новых областях.

Ваша посвященность теме автоматов и вера в их актуальность и значимость в будущем очень впечатляют. Желаю вам долгих лет творческой активности, новых открытий и успехов в вашей работе. Пусть ваш оптимизм и страсть к программированию будут вдохновением для многих других, разделяющих ваше видение и ценности в этой области.

Ну, приятно, ведь, правда :).

С таким интеллектом, как минимум, общение приятнее, чем с некотрыми натуралами. Не матерится, вежлив, не обзывается, не минусует, не дает грубых советов :) Его оптимизм и пожелания только стимулируют и создают соотвествуйщий настрой на работу :)

А alexdora отдельное спасибо за инициацию этого разговора. Получил много положительных эмоций :)

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации