Магистерский курс C++ (МФТИ, 2022-2023). Лекция 19. Многопоточность, часть 2.

Поділитися
Вставка
  • Опубліковано 18 гру 2024

КОМЕНТАРІ • 31

  • @vladalu9794
    @vladalu9794 Рік тому +7

    Отличная лекция, очень интересно. Крутой, заинтересованный преподаватель. Спасибо)

  • @ivanlosev5461
    @ivanlosev5461 Рік тому +1

    Большое спасибо за лекцию! Очень интересно

  • @Tiolych
    @Tiolych Рік тому +16

    Вдруг не видели, есть книга : Параллельное программирование на современном С++, 2022 ,Райнер Гримм. Читается полегче, чем Энтони Вильямс.

    • @allcreater_
      @allcreater_ Рік тому +1

      спасибо! Гримма еще не читал, попробую, но хочется сказать, что в любом случае Уильямс все равно хорош и достоен рекомендации :)

    • @romankrivonogov9474
      @romankrivonogov9474 Рік тому

      Прочитал коммент и моментально убежал покупать, пока есть в наличии; спасибо за рекомендацию!

  • @LuriDDD
    @LuriDDD Рік тому +1

    В примере на 41:30 метод wait сначала проверяет предикат, то есть вызов
    void wait( std::unique_lock& lock, Predicate stop_waiting );
    то же самое, что
    while (!stop_waiting())
    {
    wait(lock);
    }
    а std::cout

    • @tilir
      @tilir  Рік тому +1

      Фокус в том как это проверить. В итоге инструменты есть например снять трассу и посмотреть. Но они на удивление мало известны. Хотя казалось бы.

    • @x4da
      @x4da 2 місяці тому

      В моей трассе первый из processing тредов добегает до cond_wait до вызова cond_signal, однако, что интересно, первый (самый ранний из трех) из processing тредов из conditional_variable::wait уходит вниз в pthread_cond_wait и дальше в futex_xxx_wait, а второй processing (самый поздний из трех) из conditional_variable::wait выходит сразу

  • @sempaidebil8532
    @sempaidebil8532 3 місяці тому

    Под wsl perf можно вроде бы просто собрать, исходники есть на гитхабе в WSL2-Linux-Kernel/tools/perf. Но пока собирать не пробовал.

  • @alex_s_ciframi
    @alex_s_ciframi Рік тому

    Константин, спасибо за лекцию!
    1:09:22 - то есть, шаред мутекс будет выгоден только тогда, когда под юник лок будет что-то тяжёлое, я правильно понимаю?
    Но этого же обычно не бывает. Там всегда что-то лёгкое.
    Выходит, лучше пользоваться обычным мутексом (после измерений, конечно же :) ) ?

    • @tilir
      @tilir  Рік тому +1

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

    • @alex_s_ciframi
      @alex_s_ciframi Рік тому

      @@tilir понятно. Интересно, зачем его такой вводили тогда в язык

    • @tilir
      @tilir  Рік тому

      @@alex_s_ciframi ну вроде в POSIX тоже есть. Значит кому-то нужен.

  • @VladMazurenko-l9o
    @VladMazurenko-l9o Рік тому

    Константин Игоревич, спасибо за лекцию! А как насчет парочки доп. семинаров - факультативов по оптимизации с использованием VTune и подобного? Было бы просто замечательно :)

  • @KoSeAn97
    @KoSeAn97 Рік тому +1

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

    • @tilir
      @tilir  Рік тому +3

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

  • @test_bot5541
    @test_bot5541 Рік тому

    13:33 да вроде не ломается. UB есть в первой проверке по синей стрелке. Допустим, 100 первых потоков из миллиона проходят первый if(!resptr) , а мютекс захватил только 1 из них, остальные 99 ждут освобождения или ещё читают первое условие. Этот 1-й поток проходит 2-ю проверку, создаёт ресурс и уходит. Остальные 99 по очереди входят в критическую секцию и на 2-м ифе видят, что ресурс уже создан... И где здесь UB? :) Если вставить какой-то код между первой проверкой и lock_guard тогда плохо, но это уже будет не DCL, как я понимаю.
    UPD: тогда выходит, что проблема может быть в 101-м потоке? 1-й поток записал resptr частично, но этого хватило, чтоб 101-й уже не прошёл 1-ю проверку, но не хватило, чтоб вызвать функцию use() корректно? Если const функция, например, читает состояние, а его там нет.

    • @tilir
      @tilir  Рік тому +1

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

    • @test_bot5541
      @test_bot5541 Рік тому

      ​@@tilir как вариант при двух. Вообще, здесь всего 3 кейса:
      1) Второй процесс прошёл первую проверку и ждёт, пока первый пишет. Т.е. второй успел запрыгнуть в первый иф до мютекса. Случай проблемный в плане времени ожидания, если процессов много. Но в эту зону ожидания попадет некий % от всех , что лучше, чем если ждать будут все.
      2) Второй не прошел первую проверку, т.к. первый уже успел записать. Тут всё хорошо. Чем больше % таких процессов, тем быстрее общая работа.
      3) Второй проходит проверку, когда первый пишет. Это главная проблема, да? До того, как 1-й начнёт писать, конструктор уже отработал и вывел "c". А потом 1-й пишет в resptr. И, что бы он туда не записал, второй это преобазует в бул при проверке и проскакиевает сразу к resptr -> use(). А дальше завист от того, читает ли константная resptr -> use() внтуреннее состояние объекта, которое либо уже есть, если повезло, либо нет, тогда segmentation fault какой-нибудь. В ваших примерах resptr -> use() состояние не читает, поэтому они всегда будут работать коректно и выводить "u" после выведеного ранее "c", даже при oxрелиарде тестов. Т.е. чтоб был шанс сломать, надо чтоб use() читал какое-то поле объекта хотя бы. Вроде так.

    • @tilir
      @tilir  Рік тому +1

      Если происходит UB, мы уже не имеем права предполагать какое то конкретное поведение связанное с ним. Всё может пойти как угодно.

    • @АкакийДрищев
      @АкакийДрищев Рік тому

      @@test_bot5541 а вдруг в третем кейсе эксепшн, отключится питание или сразу же позвонит автор компилятора и попросит больше так не делать? Где гарантия, что читатель преобразует в bool то, что пишется?

    • @ivankorotkov2563
      @ivankorotkov2563 Рік тому

      Кажется проблема может быть в случае если запись указателя не атомарна. Я пытался повторить такую штуку с невыровнеными указателями (через pragma pack(1)), но даже в таких условиях получить проблемы не удалось. Мне представляется допустимый с теоретической точки зрения следующий сценарий выполнения:
      1) Поток один выделил память и инициализировал объект, и начал записывать адрес в указатель, но эта операция не атомарна и скажем он успел записать только 4 байта из 8.
      2) Поток два входит в метод, делает первую проверку, там уже не nullptr, поэтому он идет дальше без мьютекса и вызывает метод с мусорным указателем. Соответсвенно если это виртуальный метод, или внутри любое обращение к данным, то получаем SegFault.
      Добавлю, что в описанном мной сценарии я исхожу из того, что сначала должен отработать конструктор, а уже потом будет выполнено присваивание. Я не до конца уверен, что в потоке один можно полностью исключить ситуацию, когда сначала будет выделена память, она присвоится указателю и только потом на этой памяти будет вызван конструктор. Не говоря уже про возможные проблемы с кешами и реордерингами, в которых я довольно поверхностно что-то понимаю.
      Согласен, формально это UB, тред санитайзер выдает предупреждение (в двух местах - в первом чтении перед локом и при обращении после проверки), но вот лично у меня сломать не получилось ни на arm, ни на x86.

  • @tyzjar1
    @tyzjar1 Рік тому

    Про shared_mutex стало понятно, что при малом размере критической секции он сильно проигрывает обычному мьютексу. Но есть ли достойная альернатива для подобного типа синхронизации?

    • @tilir
      @tilir  Рік тому

      Рассмотрите замену мьютекса на очередь. Локфри очереди неплохи в ситуации редких записей и частых чтений.

  • @sigasigasiga
    @sigasigasiga Рік тому +5

    как всегда выше всяких похвал. большое спасибо за ваши труды!

  • @safocl9768
    @safocl9768 Рік тому

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

    • @tilir
      @tilir  Рік тому +4

      Они не плюют. Если вы идёте и доказываете что стандарт где-то нарушен это обычно рассматривается как дефект и его чинят. Я бы сказал что компиляторы ведут себя ЛУЧШЕ чем разработчики, которым иногда говоришь: вот у тебя UB, перепиши. И дальше несколько итераций "ну всё же работает" проламывания.