Механизмы синхронизации в операционных системах

Поделиться
HTML-код
  • Опубликовано: 17 янв 2025

Комментарии • 128

  • @saitaro
    @saitaro 9 месяцев назад +157

    Алексей Владимирович моргает в отдельных потоках.

    • @u2bear377
      @u2bear377 9 месяцев назад +9

      Теперь надо присмотреться, одновременно ли моргают оба глаза.

  • @КириллКорнилов-л2ю
    @КириллКорнилов-л2ю 9 месяцев назад +14

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

  • @golosbezdoka
    @golosbezdoka 9 месяцев назад +4

    Кликнул случайно. Офигел от качества. Подписался))

  • @МаксимФедотов-в6ю
    @МаксимФедотов-в6ю 9 месяцев назад +2

    Чел мега хорош, продолжай пожалуйста. Это просто бомба! Очень круто и качественно объясняешь на тем более в ру сегменте

  • @Dimoniada
    @Dimoniada 9 месяцев назад +14

    Насколько помню, в Pascal/Delphi/C++Builder-е тот же самый Motitor под капотом synchronized(), т.е. как и в С#. Пауза в конце как всегда на высоте, спасибо!)

  • @jordanmessi1
    @jordanmessi1 9 месяцев назад +7

    Очень доступно и не нудно. Спасибо за полезный контент!

  • @nikzav211
    @nikzav211 7 месяцев назад +2

    Мне скоро экзамен по ОС сдавать. Спасибо вам большое, очень доступно объяснете!)

  • @Lazarforce
    @Lazarforce 9 месяцев назад +10

    Подскажите, после Оппенгеймера какой следующий фильм будете снимать?

  • @DuplexKrokodile
    @DuplexKrokodile 9 месяцев назад +3

    Это база. Благодарю. Лайк и комментарий для поддержки и продвижения канала!

  • @madplayer5
    @madplayer5 9 месяцев назад +2

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

  • @Andrew_Shadrin
    @Andrew_Shadrin 9 месяцев назад +1

    Много слышал о необходимой осторожности при применении общих переменных, теперь понял суть. Спасибо!

  • @Dr33mway
    @Dr33mway 9 месяцев назад +13

    Вот уже с примерами кода, это прям в разы интереснее! 👍

  • @TheMrGlobus
    @TheMrGlobus 9 месяцев назад +2

    Спасибо за лекцию. Очень классно и познавательно.
    Теперь я знаю немного больше и больше понимаю как это всё работает.

  • @artemzakharov8961
    @artemzakharov8961 9 месяцев назад +4

    Огромное спасибо Вам за эти видео! Я Java-разработчик, который сам учил программирование с 0, сейчас вот собираюсь поступать в магистратуру на Computer Science и вы невероятно помогаете освоить материалы!

    • @freestylerveevo
      @freestylerveevo 9 месяцев назад

      привет, по каким материалам учился сам? или все так же с нуля (ютубы, stepik, metaint какой-нибудь)? 😊

    • @artemzakharov8961
      @artemzakharov8961 9 месяцев назад

      @@freestylerveevo привет! Я читал книгу Хорцмана "Java. Библиотека профессионала", смотрел Ютуб видео в самом начале, а когда стал крепче понимать прочитал Блинова "Java методы программирования" (она уже есть новая с Java 17 если не ошибаюсь)

  • @lycan9590
    @lycan9590 8 месяцев назад +1

    Мощь и трепет!!!! Мое почтение и преклонение. Очень крутой чел!!!

  • @sterd0
    @sterd0 9 месяцев назад +4

    Отлично, выпуск ждал. Вот реально интересно.

  • @verbs-otier-ru
    @verbs-otier-ru 9 месяцев назад +9

    RUclips плохого не посоветует. Если показывает в 4 ночи в рекомендованных, значит надо смотреть :)

  • @Daniel-c1p7n
    @Daniel-c1p7n 9 месяцев назад +1

    "Необходимо и достаточно". Чистые данные. Спасибо автору!

  • @РусланСошнев
    @РусланСошнев 9 месяцев назад +1

    Молоток!! Давай еще!!! Контент в кайф!
    Дельно.

  • @SuperArt1st
    @SuperArt1st 9 месяцев назад +18

    Съезд любителей поездов и железных дорог объявляется открытым! Have a railbuff!

  • @artorios5192
    @artorios5192 9 месяцев назад +1

    Большое спасибо! Очень интересно и полезно) Буду пересматривать)

  • @AlexAlex-jk2tn
    @AlexAlex-jk2tn 9 месяцев назад +2

    Спасибо за видео, долго просидел над примером и не мог понять где ошибка. Я не разработчик С#, я разработчик С++ и конечно бы использовал std::atomic переменные, собственно я не смог найти ошибку, но в конце когда вы объяснили в чём ошибка, я понял, как я её избежал правильно используя C++.

  • @xikkn
    @xikkn 9 месяцев назад +1

    продолжай, интересно слушать тебя, автор

  • @justcurious1397
    @justcurious1397 9 месяцев назад +5

    Превьюха космос)

  • @TimLaizaR228
    @TimLaizaR228 9 месяцев назад +5

    В шарпе так же существует слово lock, которое является сахаром для монитора, прям как synchronized в жабе. В примере можно было например написать так: lock(cm) { counter++; }

  • @dimakof
    @dimakof 9 месяцев назад +1

    Замечательные ролики

  • @dimontools
    @dimontools 9 месяцев назад +1

    Огромное спасибо за видео! Очень интересно ❤

  • @MotorBorg
    @MotorBorg 9 месяцев назад +7

    Лучший клбеикйт, что я видел!

    • @melkiy582
      @melkiy582 9 месяцев назад +1

      Согласен

  • @3955006
    @3955006 9 месяцев назад +29

    Человек снял весь материал за один дубль, потом нарезал и выкладывает нам по одному видосу ))

    • @artemzakharov8961
      @artemzakharov8961 9 месяцев назад +1

      Так это нормальная практика)

    • @ghaydn
      @ghaydn 9 месяцев назад +4

      И за всё это время ни разу не моргнул.

  • @MrCter
    @MrCter 9 месяцев назад +3

    динамические превьюшки к видео 😊 как мило...
    син-хро-ни.... 🙃 син-за-хро-ци-ни-я 😏

  • @rkc137
    @rkc137 9 месяцев назад +5

    куда делся флаг реактоса с фона 😭😭😭😭

  • @ARLX-yo1wr
    @ARLX-yo1wr 9 месяцев назад +1

    Класс!

  • @alexlm2598
    @alexlm2598 9 месяцев назад +3

    в том примере что Вы в начале показали, со счетчиком, это не Race condition, а Data race. Разница в том что сделав операцию инкремента счетчика атомарной мы эту проблему решим, а при Race condition так не получится. Например если бы мы не инкрементили счетчик, а в каждом потоке присваивали ему некое значение, то даже защитив счетчик мютексом была бы недетерминированность из за того что неясно в какой очередности отработают потоки. Советую читать англоязычную Википедию, там это все достаточно подробно описано, зато в русской пол статьи про то как американцы налажали с этим Тераком))) А так спасибо за ролики, очень познавательно

    • @abragin
      @abragin  9 месяцев назад +2

      Кстати про Терак, на самом деле 99% рассказчиков повторяют одно и тоже про пользовательскую сторону проблемы (ввели X, стёрли, ввели E, и т.п.) и отсутствие аппаратных интерлоков, а вот информацию конкретно про саму операционку, которая там работала, и про сами ошибки мне удалось найти только в официальных отчётах и немногочисленных других документах.

    • @abragin
      @abragin  9 месяцев назад

      Про data race несомненно верно!

  • @danielmilyutin9914
    @danielmilyutin9914 9 месяцев назад +3

    Привет ИУ от ФН!
    В С++ есть lock_guard (их там несколько).
    Они помогают захватить ресурс один раз и отпускают его автоматически при выходе из блока.
    Принцип известный как RAII - resource aquisition is initialization. Поэтому не надо бояться забыть V.
    С точки зрения художественной. Я бы в начало и в конец добавил "Здравствуйте!" и "До встречи!".
    А то видео кончилось просто внезапными 3 секундами молчания.

  • @mobilafilm
    @mobilafilm 9 месяцев назад +1

    Супер

  • @mndtr0
    @mndtr0 9 месяцев назад +4

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

    • @abragin
      @abragin  9 месяцев назад +3

      Тут не получится сказать "да" или "нет". Во-первых, смотря насколько далёкое будущее. Во-вторых, смотря каких разработчиков. Быдлокодеров можно заменить хоть сейчас. А стоит задать ChatGPT нешаблонное задание, и оно ничего не сделает путного. Попробуйте попросить его написать парсер CSS на C# без использования сторонних библиотек. Будет ходить вокруг да около, предлагая классы с методами-заглушками. Тоже самое и с разработчиками. Те, кто способен делать нестандартные решения будут всегда нужны.

    • @mndtr0
      @mndtr0 9 месяцев назад

      ​@@abragin спасибо за ответ! Почему я спросил: на англоязычных форумах, посвящённых программированию, на реддите все время спрашивают про это и опасаются, а я, как студент IT направления, вследствие этого тоже стал задаваться таким вопросом и даже раздумывать "а там ли я учусь, хоть мне и нравится моё направление"

  • @Lhander07
    @Lhander07 9 месяцев назад +1

    Отличное видео! Привет с РК6)

    • @abragin
      @abragin  9 месяцев назад

      Оо, РК! )

  • @jopawhand3190
    @jopawhand3190 9 месяцев назад +1

    а вы лекции не будете выкладывать больше? интересно про архитектуру NT узнать

    • @abragin
      @abragin  9 месяцев назад

      Планирую ещё много

  • @azerqgaming
    @azerqgaming 9 месяцев назад +1

    Спасибо за объяснение на языке c#, пробовал до этого Java, но .net мне больше понравился, сколько туда сейчас фишек завезли)

  • @wasd6461
    @wasd6461 9 месяцев назад +6

    я один всё время смотрел на цветные треугольники в мониторе сзади ?

    • @u2bear377
      @u2bear377 9 месяцев назад

      Кстати, да, отвлекают.

  • @semenkovalev4988
    @semenkovalev4988 9 месяцев назад +2

    А volitile поставить для переменных, которые используются в нескольких потоках? Оно же автоматически memory barrier создает и не дает кэш пробить. В данном примере, скорее всего пробитие кэша не сыграет, но в реальных случаях вполне.
    Про lock вместо голого Monitor тут уже писали.

    • @abragin
      @abragin  9 месяцев назад

      Да, пойдёт! Надо было мне про этот способ сказать 😊

    • @bdick8136
      @bdick8136 9 месяцев назад

      @@abraginЯ не особо знаком с C#, но в C++ volatile всего лишь говорит компилятору(процессору) читать данные из памяти, а не из кэшей. Это полезно для memory mapped io. Во многопотоке все равно нужны барьеры, т.к. никто не мешает другому потоку наломать дров так как volatile не делает доступ к этой переменной атомарной. В общем я бы эту глупость не рекомендовал юзать.

    • @artemzakharov8961
      @artemzakharov8961 9 месяцев назад

      @@bdick8136 Скорее всего завсисит от платформы, потому что в Java volatile даёт доступ напрямую из памяти процессора + ставит баръер

  • @Eustrop
    @Eustrop 9 месяцев назад +1

    Интересно. Но я в этой теме ожидал в конце описания синхронизации между unix процессами через операции с файловой системой.
    Ну, чтобы показать, как в реальном мире приходится выкручиваться ;)
    P.S.
    За PDP-11 - отдельное спасибо!
    Сентиментальная слеза
    Катилась
    По
    Его
    Щеке..
    2024-03-29 10:38 UTC алгоритмам

  • @BeDb_MeDb
    @BeDb_MeDb 9 месяцев назад +1

    Блин, круто)

  • @RusLSystems
    @RusLSystems 9 месяцев назад

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

    • @abragin
      @abragin  9 месяцев назад

      А что значит флаг проверки выполнения?

    • @RusLSystems
      @RusLSystems 9 месяцев назад

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

  • @homka122
    @homka122 9 месяцев назад +1

    А не подскажите, пожалуйста, как вы добились этого эффекта на 4:48? Очень интересно выглядит. Что это за софт?

    • @abragin
      @abragin  9 месяцев назад +2

      cool-retro-term, там куча различных настроек, можно по вкусу настраивать

    • @homka122
      @homka122 9 месяцев назад

      @@abragin спасибо большое!

  • @tree-service
    @tree-service 9 месяцев назад +1

    А если вместо монитора lock использовать?он тоже требует обьекта, коим каунтер не является, но можно создать произвольный объект в чем отличие от монитора?и как реализованы блокировки на субд,и если ли разница в операционных системах например мсскуэль на виндовс и на линукс и зависят ли от железа?

  • @Kolemag
    @Kolemag 9 месяцев назад +2

    Вот прям Factorio со своим дискретным принципом

  • @BeDb_MeDb
    @BeDb_MeDb 9 месяцев назад +1

    Кстати, не знаю как в C#, но вообще есть еще всякие atomic-типы/классы. Где-то гарантируется атомарное чтение/присвоение, а где-то даже простенькие операции. Но наверное да, это уже сахар всякий...

  • @NyaryanUwUrrior
    @NyaryanUwUrrior 9 месяцев назад +3

    Вы прям по темам книжки Танненбаума по ОСям идете))

    • @broken_beyond_belief
      @broken_beyond_belief 9 месяцев назад

      разве что танембаум показывает примеры на линуксовых pthread'ах на языке С. в видео примеры с кодом получше, но это имхо

  • @TheMicstep
    @TheMicstep 9 месяцев назад +1

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

    • @abragin
      @abragin  9 месяцев назад

      Напортачили, в общем

  • @skybladeby
    @skybladeby 9 месяцев назад +2

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

  • @dr1n
    @dr1n 9 месяцев назад +1

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

  • @Ssilki_V_Profile
    @Ssilki_V_Profile 9 месяцев назад +2

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

  • @Typeofundefined-mq8pj
    @Typeofundefined-mq8pj 9 месяцев назад

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

  • @AEF23C20
    @AEF23C20 9 месяцев назад +1

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

  • @baltasar9547
    @baltasar9547 9 месяцев назад +1

    Ух ты, про MemoryBarrier не подозревал, а это, оказывается важная штука, лайк

  • @non5309
    @non5309 9 месяцев назад +1

    ставь лайк, если пришел с курса по многопоточному программированию!

  • @mikhaililin3033
    @mikhaililin3033 9 месяцев назад

    Спасибо автору, очень интересный контент и хорошая подача, но возникла пара вопросов; Может ли возникнуть ситуация в которой несколько потоков ждущих разлок мутекса/семафора/другого примитива одновременно преодолеть вайл когда это произойдет? Они ведь вроде как друг от друга не зависят. И ещё, у всех примитивов в недостатках упоминается активное ожидание, значит есть варианты как без него обойтись и просто в видео нет?

    • @abragin
      @abragin  9 месяцев назад

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

  • @sheffield85rus
    @sheffield85rus 9 месяцев назад

    Скажите, ну похож ведь на Алекса Мёрфи из Робокопа?

  • @xibodohls9469
    @xibodohls9469 9 месяцев назад

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

    • @abragin
      @abragin  9 месяцев назад +1

      Про забыть вызвать - верно, это я про поддержку ключевых слов synchronized и lock имел в виду, а пример показал с явным вызовом.
      Про процессор так и сказал же: "а современные процессоры и среды выполнения типа dotnet..." 12:18.
      У потоков одно адресное пространство, когда пишем, тогда и видно.

    • @xibodohls9469
      @xibodohls9469 9 месяцев назад +1

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

  • @999poll
    @999poll 9 месяцев назад +1

    Кажется я только что понял, что означает страшное слово "прерывания"

  • @НикН-о7о
    @НикН-о7о 9 месяцев назад +3

    Огонь. Но моргайте хоть иногда.

    • @tart_meidep
      @tart_meidep 9 месяцев назад

      все-так он человек. моргнул на 8:15

  • @YouSitePro
    @YouSitePro 9 месяцев назад +8

    В превью можно было написать СИН ЗА ХРО ЦИ НИ Я :D

    • @abragin
      @abragin  9 месяцев назад +2

      Отличная идея, пробую!

    • @MSaidu-sj6vx
      @MSaidu-sj6vx 9 месяцев назад +1

      О! Название для метода подсказал!

    • @u2bear377
      @u2bear377 9 месяцев назад +1

      Звучит как название живого организма.
      "Cинзахроциния многопоточная"

  • @vechnonedovolen666
    @vechnonedovolen666 9 месяцев назад

    Как правильно пишется этот 1:43 медицинский аппарат ?

    • @abragin
      @abragin  9 месяцев назад +2

      Therac-25

  • @albion_faults
    @albion_faults 9 месяцев назад +1

    Подписался, очень круто

  • @jopa19991
    @jopa19991 9 месяцев назад +1

    Ну на представленном примере монитор так себе освобождает от ошибок типа "забыл отпустить мьютекс" :) Всё равно нужно написать enter и leave. Тут скорее подошёл бы пример с C++ным unique_lock/scoped_lock

    • @semenkovalev4988
      @semenkovalev4988 9 месяцев назад

      Согласен. В C# Monitor редко используется напрямую, как раз из-за возможных ошибок, описанных в видео. Для блокировки используется оператор lock, который содержит внутри себя перехват исключений и освобождает монитор в секции finally.

  • @evgur4
    @evgur4 9 месяцев назад

    Спасибо очень понятно объясняете , но я походу туповат и под конец уже потерял нить повествования😅

  • @Vorono4ka
    @Vorono4ka 9 месяцев назад +1

    Очень, нет. ОЧЕНЬ крутое видео!

    • @abragin
      @abragin  9 месяцев назад

      Спасибо 😀

  • @gooseob
    @gooseob 8 месяцев назад +1

    я только щас заметил что на превьюшке написано синзахроциния)

  • @awwarez
    @awwarez 9 месяцев назад

    Смотрю на некоторые алгоритмы и думаю, бедный процессор. Я бы применял бы эти локи, но в купе с паттерном Unit Of Work. Я понимаю что увеличение переменной на 1 из 2 потоков это абстрактный пример, но можно было бы увеличивать локальную переменную в потоке на 1, а каждые 100 итераций брать блокировку и увеличивать разделяемый ресурс на эту локальную переменную. После чего её сбрасывать, отпускать блокировку и продолжать.

    • @abragin
      @abragin  9 месяцев назад

      Ну тут как в анекдоте, что плац лучше метлой подметать, но поставлена задача сделать это зубной щёткой

  • @UchebavGermanii
    @UchebavGermanii 9 месяцев назад +1

    Отличное видео и объяснение.
    Думал, что пример на C++, тк counter++; и хотел спросить зачем потоки стартовать, тк в STL C++ потоки стартуются сами в конструкторе после создания, но потом понял, что это C#😊

  • @Русский-о2ш
    @Русский-о2ш 9 месяцев назад

    Класс! но звук "пш-пш"... напрягает...

    • @abragin
      @abragin  9 месяцев назад

      Что за звук? Скажите таймкод послушать

    • @Русский-о2ш
      @Русский-о2ш 9 месяцев назад

      @@abragin весь ролик... шипящие свистящие прут)

  • @brothers_pilots
    @brothers_pilots 9 месяцев назад +1

    Почему, объясните мне почему. Я попадаюсь на это видео 3 раз, и тема мне интересна, все окей, но почему я читаю название как "мемы синхронизации..."

  • @TheMicstep
    @TheMicstep 9 месяцев назад

    Автор ты не думал озвучивать антагонистов в фильмах?

    • @abragin
      @abragin  9 месяцев назад

      Мне протагонисты больше нравятся

  • @ФеофанЭпикурейский
    @ФеофанЭпикурейский 9 месяцев назад +1

    "непонятные ошибки решаются непонятными методами"

  • @leonardodavinci3737
    @leonardodavinci3737 9 месяцев назад

    Джек Степлтон?!

  • @ЦарьЕвгений-у9с
    @ЦарьЕвгений-у9с 9 месяцев назад +1

    я в этом ни чего не понимаю но то что вы говорите чтото понимаю продолжение дорлжно не минуемо следовать

  • @krotov_play
    @krotov_play 9 месяцев назад +1

    Походу надо удалять весь мой код, и писать всё ядро с \0.

  • @AEF23C20
    @AEF23C20 9 месяцев назад

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

  • @ДмитрийЛеухин-ф6ш
    @ДмитрийЛеухин-ф6ш 9 месяцев назад

    Хорошо быть роботом

    • @abragin
      @abragin  9 месяцев назад +2

      Мне тоже нравится

  • @pompei2
    @pompei2 9 месяцев назад +2

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

    • @u2bear377
      @u2bear377 9 месяцев назад

      Это не работает прямо вот так "в лоб", на уровне букв.
      Приведённый текcт получилcя практичеcки нечитаемым.

  • @ЖеняШустерман
    @ЖеняШустерман 9 месяцев назад

    а asinc await в си нет? чё-т какой-то примитивный язык для программирования медицинского оборудования (шутка, я знаю, что сама операционка написана на си) :)

  • @Gengzu
    @Gengzu 9 месяцев назад

    С таким голосом невозможно слушать

  • @kykyshka138
    @kykyshka138 9 месяцев назад

    Это всё хорошо, но очень старые подходы. Вот аккурат 60-х годов прошлого века. В окно смотрели? Там 2024 год уже. Синхронизацию данных в потоках так никто уже не делает лет 15 - 20.

    • @abragin
      @abragin  9 месяцев назад +1

      Так расскажите нам, как надо)

  • @vladislove1337
    @vladislove1337 9 месяцев назад +1

    Если бы G-Man был программистом: