Давайте разберёмся наверное с тем А какой у нас сегодня план Да что мы с вами сегодня озвучим в первую очередь мы посмотрим вообще что такое планировщик Зачем он нужен Какие он задачи решает Зачем е вообще придумали Да потом мы подумаем как его слить Что такое скели наверное Пока непонятно Но когда мы до этого дойдём у вас явно возникнет этот вопрос на который мы с вами будем совместно отвечать потом мы поговорим про все сколы и обсудим то как го работает с сим почему он определённые силы не любит поговорим про потому что очередей внутри ран тайма очень много мы это всё просматриваем поговорим про синхронизации потому что в Go Она своя врата он сам её написал да если это можно так назвать и в конце ещё поговорим про циклы как быть с бесконечными циклами как бы цепью ба нагрузкой всякими прочими как вот Go работает со всем этим Вот такой у нас примерно план на сегодня и Давайте нанм перед тем как начать Я бы хотел сказать то что я бы хоте бы вот это заня построить в виде определённой игры или определённой сценки если это можно так назвать то есть давайте мы попробуем себя возомнить немного разработчиками Гугла и представим что у нас как у разработчиков Гугла возникла определённая потребность и Нам нужен вот язык или инструмент можно это назвать так который будет достаточно хорошо подходить для разработки веб-сервисов Ну Казалось бы это логично Google такая компания где очень много различных веб-сервисов и нам просто нужен язык который видимо внутри себя ведёт определённые абстракции слой определённых абстракций сделает так что разработка веб-сервиса будет простой и в то же самое время эффективной и вот давайте мы с вами Будем пробовать на всём занятии как раз-таки вот Аля проектировать и разрабатывать этот самый язык с таким вот абстрактным ран таймов которого он будет Вот и что такое вообще веб-сервисы да в подавляющем большинстве случаев веб-сервисы это те приложения в которых преобладает бауд нагрузка Что значит её баунд нагрузка это значит То что мы много чего делаем по сети куда-то ходим в сеть ходим в базу данных ходим другие микросервисы ходим в диск читаем из диска Ну то есть у нас мало времени остаётся на какую-то компьют обработку мы ничего особо там не процесе мы там подготовили запрос пошли куда-то пришёл ответ мы получили его обратно отправили и большую часть времени я работаю с inp аутпут и вот эта нагрузка характерна тем что мне постоянно нужно делать переключение контекстов то есть мне пришёл один пользователь Я записал что-то в баз данных и я не буду простаивать Я возьму и обработаю наверное другого пользователя Да поэтому мне нужно сделать переключение контекста и вот этих переключений контекстов у нас получается очень много Ну потому что если мы пишем веб-сервис для Гугла скорее всего там будут большие нагрузки и скорее всего там будет много пользователей и придётся достаточно много менять эти самые контексты Да вот и Давайте тогда подумаем Ну мы как разработчики Гугла начинаем наверно сси определ с бейслайн с основ что ли Да мы такие думаем на напишем мы язык в котором будут потоки потоки операционной системы возьмём их абстрагируясь потоками будет достаточно просто работать сделаем определённые примитивы синхронизации там каналы и всякие фьючерсы промисы и всякое такое и просто сделаем так чтобы этим всем было просто управлять это на этом языке было просто писать и выберем например такую модель что под каждый например Connection мы будем создавать отдельный поток и Казалось бы это заработает но заработает это на небольших объёмах нагрузки Да почему Да потому что это во-первых дорого а держать отдельный поток под каждый коннекшн Ну потому что в среднем там размер потока в линуксе там до 8 Мб там 2 48 лимит - это 8 Мб то есть Казалось бы достаточно много не всегда мне на стеке Вот как раз таки потока который обрабатывает какой-то конек нужно такое большое количество памяти - это медленно потому что переключение потоков происходит на уровне ядра и из-за этого происходит прыжок в ядро Мне нужно позвонить в ядро это достаточно медленно вот Ну и к тому же наш вот этот сервис будет очень просто положить с одного компьютера Просто на создавать там я не знаю сотни десятки сотни тысяч коннекшн и сервер этот прир вот тут возможно многие сразу скажут Ну я могу сделать пул я могу при помощи Пула это всё обрабатывать Да вы можете обрабатывать Но если у меня будет один поток на он у меня будет ограниченное количество потоков то большинство пользователей просто будут ждать на входе в какой-то tcp очереди или чем-то подобном Вот и тут тогда нужно разобраться почему тогда вот медленно медленно работает этот самый контекст свин Ну то есть в операционной системы есть энное количество потоков и скорее всего там не один веб-сервис запущен там ещё запущено множество других различных процессов Да которые выполняют ту или иную полезную работу вот операционной системе есть планиро обще который берёт и меняет контексты этих самых потоках которые выполняются внутри этих самых процессов Как он это делает Ну он по сути у потока что есть у него есть определённый контекст поток - это просто контекст для выполнения какого-то кода Мне нужен регистр Мне нужен instruction Pointer то есть чтобы понимать какую инструкцию выполнять Мне нужен стек чтобы выполнять какой-то программный код чтобы вызывать функции и так далее вот по сути весть контекст который есть у потока если я себе возом планировщиком операционной системы если я хочу снять один поток с исполнения Но что мне нужно сделать мне нужно снять регистры положить их допустим на стек того потока который я снимаю и взять регистр другого какого-то потока поставить загрузить их в процессор и начать выполнять какой-то код Ну и конечно же с использованием другого стека И вот всё это работает но это работает достаточно медленно потому что всем этим занимается ядро операционной системы вот более того есть неочевидные накладные расходы неочевидные накладные расходы с чем связаны Ну если у меня меня допустим был поток который выполнял определённую функциональность и я переключаюсь на другой поток то в мо в моём процессоре есть кэши которые были заточены на выполнении одного кода в рамках одного потока когда я переключаюсь на другой поток там скорее всего абсолютно другой код абсолютно другие данные и Казалось бы кэш вымывается Да помимо этого если я переключаюсь на поток того процесса который долгое время у меня уже не выполнялся если у меня включён свопинг то часть страниц виртуальной памяти уже будет выгружена диск мне придётся с диска это поднимать что тоже будет работать не быстро очевидно Ну и много чего ещё вымывается те буфер вымывается конвейер процессора вымывается в общем работает это медленно достаточно Да И вот кажется что вот когда мы разрабатываем какой-то язык или инструмент для разработки веб-сервисов это наверное самая большая проблема в которую нам нужно целиться как инженером да нам Нужно постараться минимизировать как раз-таки накладные расходы на контекст свитчинг потому что ещё раз повторюсь контекст свитчинг будет очень много если мы работаем сё бауд нагрузкой а возникает вопрос у нас как у инженеров Да как сделать эффективнее что можно придумать чтобы как раз-таки ну не терять Вот это время потому что ещё раз покажу вот эти вот контекст свитчинг здесь в принципе я ничем полезным не занимаюсь я не выполняю никакие инструкции это просто Бесполезная трата времени ресурсов моего процессора точнее ресурсов моего процессора и вот вопрос как сделать эффективнее Ну давайте подумаем если у пото вот контекст потока Это всего лишь регистры истк то что мы можем сделать так давайте сами создадим свои контексты внутри нашего приложения Ну то есть у меня будет определённое приложение и я в рамках это приложения я могу снять регистры Да могу но мне потребуется алер Ну да придётся там на ассемблере чуть пописать я при помощи Амра смогу снять эти самые регистры запушить их в стек смогу смогу я смогу заранее подготовить другой какой-то контекст Да смогу в принципе про подготовлю какую-то функцию исходя из которой я начну выполнять тот или иной код просто в instruction Pointer это загружу и начну исполнять этот код и по сути Вот этих контекстов я могу на создавать энное количество у себя внутри программы и получается так что ядро операционной системы ничего не будет знать о том что у меня есть какие-то легковесные потоки внутри ядро операционной системы знает только лишь про поток но оно не знает то что я вот мы вот такие вот я не знаю находчивые программисты и решили сами делать контекст свинг только на уровне приложения имея при этом аем да грубо говоря снимать регистры пушить на стек заново поднимать регистры другого какого-то легко весно потока и начинать выполнять этот код и здесь тогда что получается но получается то что это может быть эффективнее Почему во-первых Потому что ядро операционной системы не знает ничего про котек свинг то есть на нашем уровне он будет конечно же работать в разы быстрее потому что он будет работать в users спейсе а не в спейсе во-вторых то есть стек потока в среднем Ну где-то до 8 Мб 4-8 МБ Может быть но можем ограничиться 8 Мб Казалось бы достаточно много не всегда под какой-то коннекшн Мне нужно столько А стека да столько необходимого пространства на стеке мы можем в принципе взять и для нашего легковес вот этого потока сделать какой-то минимальный размер стека например начать его с 2 КБ Ну и постепенно расти в случае необходимости если мы проведём это сравнение вот даже допустим у меня будет 10.000 временных соединений и если использую модель когда на каждый конек у меня выделяется поток Ну в среднем приблизительно это где-то 80 ГБ оперативной памяти мне потребуется что Казалось бы много да и дорого а если я в принципе начну с 2 КБ Под каждый конек у меня получится 20 Мб оператива при использовании вот этой как раз таки модели с рутина Казалось бы это эффективнее раз с точки зрения использования памяти два с точки зрения выполнения Ну почему Да потому что опять-таки повторюсь вот этот контекст свинг он работает внутри р спейса про него планировщик операционной системы не знает Он просто знает про вот этот поток внутри которого у меня будет происходить контек свинг из-за того что он работает в users спейсе он конечно же будет выполняться эффективнее Вот и здесь Наверное мы подходим к основам к основам того что ж вообще такое планировщик Go Что такое R Go и Раз уж мы подходим к этим самым основам то мы как разработчики Гугла в данной ситуации когда мы разрабатываем или пишем какой-то новый язык программирования С каким-то ран тайм который будет достаточно просто решать вот эти вот самые задачи по разработке веб-сервисов нам важно осознать ключевую идею в чём будет ключе Ключевая идея языка нового программирования потому что этих языков там сколько сотни наверное уже да существует Зачем делать ещё один нам нужно определиться с ключевой идеей Ключевая идея какая будет Мы не дам в руки программист операционно системы делаем Это максимально просто Зачем им знать про какие-то потоки делаем вид что есть только рутины Ну то есть мы хотим делать так чтобы пользователь управлял только лишь рунами только легковес потоками но важно ответить мы делаем вид что он управляет только лишь легковес потоками так или иначе это всё конечно же выполняется на каком-то системном потоке операционной системы и всю сложность распределения рутин Ара есть сы пользователь об этом заго он ими управляет Под каждый конек создаётся этот легковесный поток Он сам ещё может создавать легковесные потоки в случае необходимости и кажется это интересная идея которую можно сейчас попробовать Нам начать развивать как разработчикам Гугла вот а что что мы делаем мы начинаем наверное с N к одному модели что это за N к одному модель Ну то есть у меня есть поток определённый Да операционной системы и у меня есть рутины которые выполняются на этом потоке но я не могу сказать что они выполняются на каком-то потоке поток просто выполняет какой-то код и в рамках выполнения этого кода Я просто делаю контекст свинг на эту рутину Ну то есть абстрактно да можно сказать что рутина выполняется на потоки на самом деле просто поток исполняет код этой рутины вот и всё и в данной модели что получается Мы выполняем энное количество рутин на одном потоке То есть у меня получается отношение один ко многим да в данной ситуации и рутины мои хранятся в определённой очереди Ну то есть рутина какая-то по выполнялась я допустим беру я не хочу е больше выполнять Как как НТА этого языка я её добавляю в конец очереди Потом беру другую рутину из начала очереди выполняю снова добавляю в конец очереди кажется что пока в принципе идея достаточно проста и понятна Да но здесь тогда возникает вопрос А как и когда будем вытеснять эти самы рути есть а мы говорили про контекст свитчинг Когда нужно много часто переключать как раз-таки эти самые рутины и очевидный способ грубо говоря переключать эти рутины при си сколах Да при обращениях там к диску к чему бы то ни было Ну как быть просто с программным кодом когда у меня происходит определённое выполнение какого-то кода я выполняю бизнес логику определённые моменты мне хотелось бы переключать Да И вот планировщик операционной системы он у нас а получается вытесняющие То есть он в любой момент может прийти сказать поток давай-ка Ты уходи в сторону все дра сейчас встанет на это место другой поток но мы как разработчики Гугла начнём наверное с простого то есть проще реализовать кооперативную многозадачность но если мы явно дадим программисту определённые инструкции допустим Wild когда он пишет или Go Run SH R SH точнее чтобы он явно в каких-то местах добавлял в код контекст для того чтобы переключиться на другую рутину кажется что вот простота Астра нашего языка она удт куда-то на второй план хочется сделать это просто ну то есть начать с кооперативной многозадачности сделать чтобы программисты об этом не задумывались Да поэтому что мы можем сделать Ну если мы пишем язык давайте сделаем так чтобы компилятор когда компилировать в разных языках програми есть онная рока кода для переключения контекста на следую Тину и это Мола в прологе функции то есть в ранта Go если мы начинаем свой стек допустим с 2 КБ очевидно в какой-то момент нам этого стека может не хватить нам нужно определённое условие для роста проверить Хватает ли нам места для стека или нужно грубо говоря взять и добавить определённое место на стеке и почему бы вот эту логику туда не добавить где мы проверяем что нам нужно увеличить мой стек А я возьму ещё и проверю А нужно ли мне сделать Конте свинг на другую кератину или нет и кажется с этого пока мы в принципе можем начать едем на кооперативной многозадачности просто для нас как для разработчиков потому что вытесняющие многозадачность Ну труднее реализовывать Это очевидно в то же самое время это проще для программистов который будет писать на нашем языке потому что им не нужно будет явно не писать R Go или уды какие-то Да если мы говорим про другие языки программирования а но здесь возникает Такая сущность как масштабирование не сущность Аде то есть когда мы пишем какой-то софт хорошим критерием это софта является такое свойство как масштабируемость то есть а Смогу ли я слить какой-то софт Да ну скели при помощи чего вертикально когда я добавляю какое-то количество ресурсов либо горизонтально когда грубо говоря я разношу этот софт по разным например компьютерам Да И вот здесь возникает как у у нас как у инженеров Гугла такой вопрос как быть с с многоядерными процессорами Ну то есть грубо говоря представим что у нас код наш код выполняется на серверах глох Да есть многоядерные процессоры и хотелось бы делать так чтобы Код Этот достаточно просто слился на всё это количество ядер которое у нас существует Да в том или ином сервере хорошее замечание Давайте его подумаем Как можно сделать Ну в данной ситуации мы как инженеры Гугла сразу можем предположить что давайте мы сделаем модель модель это что будет у нас будет эм мное количество потоков да то есть потоков операционной системы мы сразу заводим это количество то есть определённый тред пул на фиксированное количество потоков и у нас есть энное количество рутин то есть отношение многие ко многим меня получается и в этой очереди у меня есть рутины которые просто по очереди будут доставаться тем или иным потоком и исполняться Хорошая идея хорошая концепция звучит хорошо но Давайте дальше думать оказывается что это не особо масштабируемый подход почему это не особо масштабируемый подход Ну потому что когда у меня потоки обращаются к какому-то определённому участку памяти и когда хотя бы одно обращение из них модифицируют что мы делаем мы как правило сразу не задумаемся оптимизации мы по-простому да всё делаем итеративный путём мы понимаем Ну если мы возьмём к в данной ситуации что будет ну будет очень плохо Ты треда постоянно бьются КС постоянно там спотыкается на нём блокируется на нём восемь ядер ещё всё будет хуже 64 ядра ещё хуже не скели этот подход Ну давайте думать что дальше Ну разработчики Гугла думают О'кей давайте сделаем не блокирующие примитивы синхронизации давайте сделаем Look fre очередь на Томи как напишем да А идея хорошая концепция интересная но опять-таки КФ структура ф очередь точнее очередь - это такая структура данных у которых по сути два входа и начала Ну то есть мы пишем в конец читаем из начала два входа в неё и опять-таки Котен у этих потоков тоже будет достаточно большим они постоянно будут друг другу мешать они постоянно на каспе будут прокручивать ш конн и всякое такое будет эффективнее чем с использованием потоков скорее всего Но всё равно ш cont Даст о себе знать думаем дальше что можно делать Да и тут внезапно знакомый разработчик который сидел за соседним лом пригает из и говори отделе недавно базу данных порвали была одна база данных которая работала на одном компьютере взяли её нарезали и разнесли по нескольким компьютером и зали это всё да замата биро И тут у нас как у разработчиков возникает идея давайте поем Давайте возьмём и нарежем эту очередь на кусочки и что это нам даст То есть у нас будет появляться ное количество Но точнее количество очередей будет закрепляться за каким-то потоком вот и получается то что поток будет обращаться только к его локальной очереди И тем самым мы снимаем как раз таки проблему того что у нас будет избыточная синхронизация Да ну то есть когда все будут упираться в тот или иной примитив синхронизации и Давайте введём ещё одну такую концепцию как процессор Что такое процессор процессор по сути это определённый абстракция которая будет говорить о том что у меня есть определённый ресурс для выполнения того или иного кода что это за ресурсы Ну это грубо говоря очередь рутина Вот и у меня получается следующее у меня получается теперь gmp модель Что такое gmp модель когда у меня есть рутина то есть рутина - это то что мы исполняем Ну что очевидно Да её вот программный код который мы который напишут разработчики которые будут использовать наш язык программирования вот есть машин Это где мы исполняем Ну то есть поток операционной системы Да потому что опять-таки всё выполняется в рамках потока Ну то есть код рутина он выполняется в рамках потока операционная система ничего не знает про рутина она исполняет код потока а то какой мы код подкладываем туда рутины либо одной либо другой это уже Наша задача Ну то есть так или иначе он исполняется только лишь на потоке и есть процессор процессор опять-таки Ну это права ресурсы для исполнения Ну ресурсы Что такое ну очередь вот как раз-таки та очередь где у меня находится те сущности которые я буду исполнять и у меня как раз-таки получается такая связь что один машин один пото явно соотносится с одним процессором То есть у них связь один к одному то есть один поток вот здесь соотносится только лишь с одним процессором вот а давайте двигаться дальше здесь тогда у нас как у разработчиков гла тоже возникает Следующий вопрос Ну то есть мы идём итерация Да и такая такой вопрос в какую из очередей добавлять новую рутину то есть мы как разработчики взяли и Дали пользователям возможность создавать новые рутины при помощи Go и передавать какую-то функцию потом из этой функции начнётся выполнение кода в рамках уже другой рутины но в какой процессор то есть Какую очередь процессора добавлять эту рутину наверное вопрос очевидный будем добавлять локальную очередь то есть грубо говоря если у меня в рамках этого потока вот эта рутина создаёт ещё одну рутину Ну я добавлю её в локальную как раз таки очередь то есть локального процессора что очевидно То есть у меня есть вот только онная очеред туда я буду добавлять Окей Следующий вопрос возникает А что делать но это логичный вопрос и правильно что у нас возникает как у разработчиков Да языка Что делать если в очереди закончится рутины вот логичный вопрос и Давайте тогда думать что делать а есть две концепции первая концепция называется working working - это та концепция когда потоки они шарят определённые рутины между другими потоками Ну то есть в данной ситуации что у меня получилось у меня был поток закреплённой очередью процессором к этому потоку внезапно у него закончились рутины для исполнения может быть такое Да может одно из решений проблемы будет то что потоки периодически будут брать и отгружать сть рутин в други очереди Да нар Это тоже не совсем хорошее решение потому что Как понять когда нужно отгружать сть рути Мне кажется здесь будет такой подход что просто определённые потоки будут зря тратить определённые инструкции зря кому-то пробовать отгружать рутины Когда у них уже есть какое-то количество рутины поэтому Work shing в данной ситуации не особо хорошо подходит Но это паттерн в то же самое время широко распространи кажется что здесь лучше нам как разработчикам этого языка использовать другой подход под названием Work stealing А в чём разница шеринга от Work слинга Work Стилинг подразумевает то что потоки Не сами будут отдавать какие-то рутины Ну то есть раз в определённое какое-то время да а наоборот те потоки у которых нечего выполнять то есть нет рутины нет контекста пользовательского для исполнения они будут ходить в другой процессор то есть ну в другую локальную очередь и оттуда получать эти самые рутины Казалось бы интересно Казалось бы просто но здесь важно понять что ЕС мой поток вре работает другой поток операционной системы конечно же здесь нужна синхронизация опять-таки мы первоначально уходили от проблемы когда у меня была глобальная какая-то очередь когда там была либо к либо Lo fre и мы боролись с Котен Да здесь опять-таки магия не случиться Мне нужна синхронизация чтобы работать с этой как раз-таки общей локальной очередью Да но в чём идея вот эта общая очередь она будет общей только тогда когда у какого-то потока закончится рутина и мы предполагаем что такая ситуация наверное будет происходить нечасто поэтому можем на это пойти вот мы думаем на тему того как разработчики какой примитив синхронизации использовать к или ф Ну и делаем свой выбор в пользу ф что будет работать быстрее кажется что в данной ситуации вот а дальше вопрос тоже разумный тоже по делу У какой именно очереди красть Ну то есть у меня как у потока закончились какие-то рутины в какую мне очередь пойти какой очереди мне украсть эти самые рутины немного подумали и Давайте выбирать очередь рандомно ну то есть подкинули монетку Какая монетка выпала туда попробуем взять оттуда и украсть другой вопрос тоже разумный Что делать если в другой в другой очереди тоже нет рутин снова разумный вопрос снова думаем насчёт этого вопроса но опять-таки как пишут веб-сервис Но если какой-то ресурс в сети не доступен что мы сделаем Давайте попробуем прои прора какое-то количество раз и попробовать снова до него Достучаться и поэтому Давайте попробуем украсть четыре раза Почему четыре раза Ну Казалось бы это не слишком большое не слишком маленькое число если уже четыре раза никуда не получилось Достучаться Ну обсудим дальше что с этим будем делать Вот а дальше сколько красть из другой локальной очереди то есть Окей пришли в другую локальную очередь а там есть рутины Сколько брать 1Д 5 10 20 30 ну то есть размер локальных очередей он ограничен там 256 всего лишь рутин может быть Вот сколько забираете да давайте красть половину Почему будем красть именно половину а не одну или не две опять-таки из-за Конте чем чаще мы будем ходить по чужим очередям тем чаще мы будем мешать чужим потокам тем чаще будет Котен тем чаще они будут спотыкаться будет синхронизацию Давайте ходить реже А как реже делать Ну прийдём и возьмём сразу половину Вот и так как раз таки будем минимизировать наш самый Котен когда несколько каких-то определённых потоков мешают друг другу вот что возникает дальше дальше мы как разработчики Гугла думаем Ну окей мы написали написали тот язык который просто позволяет управлять определёнными враль разработать язык то есть инструмент который будет круто подходить для написания веб-сервисов сервисы подав большинстве свом случае это нагрузка нагрузка это постоянные сикол То есть это сисла ну и нужно как-то подумать А как быть с этими самыми силами да Потому что если мы сейчас всё оставим будет работать крайне неэффективно почему это будет работать крайне неэффективно то есть вот моя модель Вот они локальный очередь очереди очереди и допустим вот правому потоку рутине который На на нём выполняется нужно произвести какую-то сетевую операцию сетевая операция - это сил сил - это прыжок в ядро Что значит прыжок ведро прыжок ведро - это значит То что пользовательский поток точнее поток операционной системы он блокируется Ну то есть он переходит в состояние Вейн планировщик операционной системы снимает его с выполнение на ядре ставит какой-то другой поток опять-таки контекст СН происходит от которого мы старались раньше убежать Да ну то есть мы старались убежать контекст свинга пото а здесь он как раз таки берёт и проходит мы прыгаем в ядро операционной системы а интересная задача интересная точнее проблема которую нам сейчас нужно будет постараться решить А вот как тогда её решить Давайте делать Так что как раз мы пишем код на Go Ну точнее мы разрабатываем язык программирования go в нашей воле есть компиляторы в нашей воле Ну можно сказать мир под ногами Да и мы же можем понять когда грубо говоря происходит тот или иной сиско мы можем просто это обернуть а перед в этой самой оберткой пере вызовом Си сколом Ну что мы можем сделать мы можем открепить поток от какой-то локальной очереди то есть от какого-то процессора мы берём открепляться на каком-то си сколе но у нас здесь есть другая очередь не другая точнее Вот эта самая очередь рути мы можем создать е один поток который будет выполнять эту очередь рутин то есть какая Здесь проблема мало того что поток заблокирован вот эта очередь рутин она голодает ей никто не занимается А вдруг там какой-то важный код вдруг там я не знаю код по охлаждению реактора Да ну вряд ли конечно код по охлаждению реактор будет написано Go Вот Но тем не менее и здесь она ситуация это концепция лучше то что у нас появляется новый поток который занимается тем что выполняет код рутин который находится в этой самой локальной очереди да вопрос тогда возникает А что делать с этими потоками То есть у меня поток который выполнил какой-то сил я не знаю прочитал из сокета либо записал в сот он закончил эту операцию Что делать дальше давайте мы не будем удалять этот поток Ну потому что создание потока не такая быстрая операция давайте мы зарезервировать туда потоки Зачем будем отгружать потоки но когда вот этому потоку снова прини сно процессора и нам потребуется новый поток мы его не будем создавать у нас уже будет готовый поток всё что нам нужно будет это его разбудить и прикрепить к этому процессору кажется интересная концепция Да интересная идея Вот и вопрос тогда А вот когда выполнится сикол куда добавлять эту самую рутину Точнее не этот вопрос я забежал немного вперёд вот здесь есть особенность которую мы как разработчики Гугла то есть будущего как раз-таки вот языка хотим предусмотреть есть определённые силы которые выполняются крайне быстро так называемые shorts И зачем мы будем открепляться кажется если быстро выполнится Ну окей пускай он быстро выполнится поэтому давайте сделаем так что планировщик Go сразу открепить поток от процессора если поймёт что поток будет заблокирован на системном вызове в течение долгого времени например ну пот писать или из файла читать там или что-то подобное вот в других случаях Ну то есть Short Life все сколы он позволит потоку быть заблокирован и не будет открепляться от процессора то он же Может достаточно продолжительное время Ну я не знаю быть в блокировке Да находиться потому что в операционной системе есть другие ещё потоки Вот и и планировщик операционной система может определённое время не ставить этот поток на исполнение Да вот как быть в этой ситуации давайте мы как разработчики Go опять-таки введём определённую абстракцию разработчики которые будут писать на языке Go про него ничего не будут знать Но это будет просто под капотом нашего ран тайма находиться заведём отдельный поток системный назовём его Симон как бы системный монитор что он будет делать он будет пере периодически приходить на этот поток который находится в сиско если он ещё не ожил спустя определённый интервал времени то мы его выясним выясним от процессора точне не вым от процессора скорее здесь будет сказать от крепим от процессора создадим новый поток который прикрепится к этому самому процессору То есть ещё раз если мы понимаем что сикол будет продолжительным Мы сразу берём открепляться отдельный поток который называется Симон Вот давайте двигаться дальше Тогда вопрос снова у нас очень много вопросов сегодня возникает да А куда девать рутину После выполнения сисла Ну куда непонятно да давайте сделаем так пока просто мы добавим рутину в очередь того процессора где она была ну то есть Можно у прикреплять онный контекст была кажется не проблемам язы сделать Ну просто добавить определённый стейт туда Да и потом понять В какой процессор её Нужно вернуть кажется что это не проблема вот Но если процессор недоступен например из-за того что заполнена локальная очередь там допустим 256 capacity и capacity под полку заполнено непонятно куда девать или если поток связанный с процессором выполняется скол если мы добавим в эту очередь но она там ещё но она будет голодать определённое время да то есть дольше будет дольше будет не выполненной я бы так мог бы это объяснить то мы будем искать другой процесс но если мы не нашли доступных процессоров то добавляем тог рути в глобальную очередь Давайте возьмём тогда и заведём ещё глобальную очередь глобальную очередь как раз таки для этих случаев когда непонятно куда уже добавить эту рутину это будет своего рода как отстойник для рутин Да ну не знаем куда добавить рутину воу Вот и опять-таки попе усложняется То есть у меня теперь появляется у меня появилась глобальная очередь у меня появился ещ отдельный поток Симон Хотя Казалось бы я начинал просто с того что у меня US потоки будут выполняться на каком-то системном потоке операционной системы Да вот и здесь тогда с смотрим такую ситуацию если в моём сервисе открыто 10.000 соединений то потенциально В моём приложении может быть создано более 10.000 потоков почему Ну потому что если грубо говоря я беру и делаю вызов read на сокете как вот здесь у меня открепляться поток Ну то есть Аля создаётся новый Да если у меня нет врид пуле А вы за врид очень ну продолжительное время может не заканчиваться потому что мне клиенты могут долго не писать да и возможно такая ну я не знаю самая плохая ситуация когда у меня есть 10.000 кокшенов мне ни один из клиентов не пишет И у меня создавалось 10.000 потоков может быть вполне вот в самом худшем случае потом нужно ловить бак репорты от пользователей который программируют на этом языке программирования А и В данной ситуации но опять-таки та же самая проблема если размер стека потока в среднем 8 Мб потребуется Ну примерно 80 МБ оперативной памяти для этого всего контекста вот что делать Ну давайте воспользуемся инструментом мультиплексирования в операционных системах то есть в линуксе это л в Маке это в ЧМ идея этого инструмента Ну то есть системный вызов он блокирующий то есть если я вызываю например вызов и читаю из какого-то сокета если данных нет сокета поток мой заблокировался его снимает планировщик операционную системы ставит какой-то другой поток давайте сделаем не блокирующий системный вызов Ну то есть если мы пытаемся вызвать никах дан мы просто так бы эти сделали не блокирующие вызовы мы могли бы полить Да периодически брать и опрашивать Рид Рид когда он не вернул ошибку но значит то что данные пришли можем их прочитать Казалось бы интересная идея но мы будем тратить CPU на это всё Ну то есть полинг полить тот соке который Возможно там я не знаю через минуту придут какие-то данные если коннекшн конечно не порвётся А можно сделать лучше можно использовать мультиплексе Ну то есть опять-таки в Linux - это epol в Маке это CQ Что это такое Ну берём когда Мы открываем какое-то сетевое соединение сокеты или или что Ну сот в данном случае да наверное некорректно будет назвать что-то подобное Потому что всегда сетевое соединение - это сот и по сути сот - это файл дескриптор мы берём добавляем файл дескриптор в этот самый мультиплексе и вместо того чтобы нам самим полить периодически это всё обходить пробовать прочитать прочитать не блокирующим способом Давайте эту обязанность отдадим вот этому самому мультиплексе когда придут данные в тот или иной файп когда он будет готов для чтения и запи он нам отправит Сина то есть показывает вот здесь у меня есть данные ты вот здесь можешь прочитать Казалось бы эта реализация нам подходит то есть мы в данной ситуации просто берём и в одном из потоков пишем Event loop наверное Кто пришёл из жава скрипта знаком с этой концепто То есть у нас в одном из потоков крутится Event loop Вот и когда получается у меня будет происходить сетевой вызов там я не знаю вместо того чтобы его отгружать на отдельный поток я его отправлю на NP NP вот этот файл дескриптор на котором я хочу осуществить операцию записи или чтения Ну то есть это сот под капотом не сот под капотом наверное абстракция над файлом дескриптора который есть сот этот этот файл дескриптор просто добавится ВЛ Вот и файл дескриптор будет сопряжён с этой рутиной когда получается у меня придут данные но мы можем разблокировать эту самую рутину и вернуть её А куда возвращать ну после завершения сетевого вызова наполе возвращаем в очередь по такому же сценарию как и при хфе когда мы отплясывать Ну то есть у нас появилось очень много очередей Да и как с ними быть и наверное возникают следующие вопросы вот как быть теперь с рутина в глобальной очереди ведь потоки они идут и берут рутины только из локальных очередей очевидно изза этого получается что рутины в глобальной очереди будут голодать Ну то есть starvation Уменя определённый получается как быть с такой ситуацией Ну давайте что сделаем наверное вот этот участок кода многим знаком многие видели то есть мы один раз в 61 тик шедулер Что значит 61 тик шедулер это просто сколько раз мы обращались к шедуле мы ведём счётчик произошло 61 обращение мы пошли забирать глобальную очередь Почему 61 число опять-таки математически доказуемо но вряд ли вам докажу это не слишком большое не слишком маленькое число которое будет приводить к редким контентов спотыкаются как раз таки вот одну общую очередь вот этому будет происходить только один раз один раз в 61 обращение к шедуле других ситуациях если мы нашли грубо говоря в локальной очереди то мы пытаемся украсть из других очередей если не нашли то мы проверяем глобальную очередь если там ничего нет то полим сеть примерно такая концепция у нас получается Вот и тут вопрос логичность но возникает у нас как у разработчиков а как быть с Шами когда несколько рутин берут общую блокировку или отправляют данные друг другу через канал что я поем под Шами если эти несколько берут общую блокировку скорее всего они выполняют какой-то общий код если они Пересылаю данные общие друг друга скорее всего у них общие данные которые Казалось бы логично можно кэшировать в ядре процессора вот А наша концепция не особо дружелюбная к ям к ядрам процессора почему Ну потому что есть фо очередь ну то есть кто не знаю можно привести налоги в стандартную очередь там очередь банкомату или магазину Кто первым пришёл первым об служится и вот допустим вот эта вот первая рутин она взяла какую-то блокировку что-то выполнила она попадёт потом в конец этой очереди если она снова потребуется ей прочитать из из той секции которая под блокировкой пока вот здесь до неё дойдёт время но кэши протухнуть у процессора Да что Казалось бы неэффективно есть второй подход он называется лифо ну то есть стек стопка книг там стек вызовов и всякое прочее да а Казалось бы он очень дружелюбный для кэша потому что вот рутина которая только выполнила она сразу в конец этой самой очереди попадает и снова оттуда достаётся да но это не особо Fair то есть не особо справедливый подход потому что вот эти рутины стеки они будут голодать до них очень долго не будет доходить время ну не не время точнее дело до выполнения Да поэтому в данной ситуации Что можно сделать Ну давайте гибрид сделаем давайте сделаем очередь фифо и сделаем так называемую однокомпонентную очередь фо Зачем нужна эта однокомпонентная очередь фо Ну то есть опять-таки та ситуация когда у меня вот только-только одна рутина по выполнялась я возьму и добавлю её в лифо если в лифо есть какая-то рутина она просто вытесняется в фифо по такому же самому принципу когда у меня рутина закончила выполнение она вытесняется в лифо и берёт ту рутину которая находится в лифо если там есть а вот как раз таки эта ситуация когда несколько рутин между собой общаются будет очень дружелюбно потому что кши скорее всего будут проты Вот но здесь тогда возникает ещ один вопрос ещ одна проблема как быть с циклами при общении рутин Через несколько каналов Ну то есть когда две рутины очень долго ставят друг друга в лифо Ну то есть например есть две канала есть две рутины одна рутина пишет Первый канал вторая рутина читает из первого канала пишет во второй и вот так они в цикле друг друга гоняют и получается то что они по очереди будут друг друга в лифо выставлять а фифо при этом будет голодать проблема то есть очень много у нас проблем возникает в процессе всего этого да Как избавиться от этой проблемы давайте считать время непрерывной работе то есть непрерывной работе Вот в этом как раз таки цикле когда каждая грудина друг друга берёт и меняет в этом самом однокомпонентной лифо и в случае превышения временного лимита мы вытеснили кто будет читать но у нас есть Симон системный монитор который как раз-таки уже занимается тем что периодически опрашивает потоки при Short Life слов на тему того не готов ли этот поток дальше для выполнения или пора его открепить от процессора Пускай этим и занимается Вот здесь снова возникает Следующий вопрос а а как быть синхронизацией Ну то есть мы дали пользователям нашего языка программирования который мы с вами пишем достаточно простые инструменты То есть он ничего не знает о системных потоках он только знает про Вот таких вот легковесный потоках он может их запускать он знает то что для каждого конек создаётся вот этот у меня один легковесный поток А если он захочет писать какой-то Ну писать какие-то разделяемые структуры данных какие-то глобальные данные там шарить контекст между несколькими рутина то есть в определённой ситуация рано или поздно ем потребуется синхронизация синхронизация в виде чего Ну в виде мтек сов и других разных всяких примитиво синхронизации Да как быть с этим наивно что мы можем сделать Ну давайте возьмём и пользователю просто Запроси вызовы системных примитиво синхронизации не знаю те же самые мьютексы да то есть МК которы работает на уровне операционной системы сделаем так то мы опять вернёмся к той же самой проблеме что у нас поток будет блокироваться при мьютекс оказалось бы нам этого не нужно делать зачем мы будем блокировать поток Но если вот эта тинка заблокировалась Давайте её вытеснен и будем выполнять но я не хочу блокировать вот этот поток поэтому примитивно синхронизации которые работают на уровне операционной системы мне не подходят это будет крайне неэффективно работать поэтому нам как раз программирование Что нужно сделать Ну мы много чего Пишем с нуля Давайте напишем свои примитив синхронизации напишем те же самый мьютексы поверх мтек сов ещё куча всего там придумываем Как написать мьютекс что нам потребуется намм потребуется определённая ячейка памяти например атомик или какая-то целочисленная ячейка памяти которая будет символизировать о том что наш поток заблокирован О'кей если мы оставим только одну ячейку памяти у нас получится спилок который просто в цикле ожидания будет проверять условия этой ячейки памяти Поэтому если мы мы пытаемся взять тек если он заблокирован нам просто нужно будет обратиться в планировщик не операционной системы а Go То есть просто вызов планировщик который явно вытесняет эту самую рутину Вот и ещё перед тем как вытеснять Можно попробовать прокрутить несколько раз klop Ну то есть Аля Как устроены ютекс в операционных системах мы несколько раз Допустим или один раз попробовали прокрутить klop если у нас не вышло ну О'кей тогда идём в планировщик Go и вытесняя эту самые рутины Ну примерно так взяли и написали эти самые ютекс и у нас получилось теперь то что у нас блокируется не поток потому что опять-таки до этого планировщик операционная система ничего не знал про наши рутины сейчас а операционная система ничего не знает про какие-то наши мьютексы которые мы взяли сами написали Да вот и поэтому у меня что блокируется у меня блокируется рутина когда она заблокирована поток её вытесняет и поток готов взять други другую рутину из очереди для выполнения Да что Казалось бы здесь на самом деле линия только лишняя указана осталось из предыдущего слайда вот здесь и вот здесь Вы не обращайте на неё внимани забыли удалить вот эта рутина никуда не идт ВПО вот Давайте тогда как у потоков есть определённое состояние давайте мы как разработчики основа языка Go введём состояние рутин будет три состояния ранин который вот рутина которая сейчас выполняется Ну то есть вот рутина выполняется рутина кото находится вот здесь в очереди Она готова к выполнению и состояние она ждёт чего-то допустим она заблокирована на мьютекс либо заблокирована на сиско на каком-то да то есть она ждёт чего-то Тогда вопрос вот заблокированы рутины куда вытеснять Казалось бы нелогично вытеснять их в эти очередям почему Ну потому что здесь у меня как бы готовы рутины к выполнению если у меня будут здесь не готовы Я просто зря буду брать какую-то рутину делать контекст свитчинг на неё когда Казалось бы она абсолютно не готова Да наверное нужно отдельное место куда их вытеснять вот Ну да Вот как раз таки этот вопрос Куда их вытеснять отдельное место давайте сделаем ещё одну очередь на мало очередей у нас было были локальные очереди у нас есть глобальная очередь но эти все очереди рано был рутин рутин готовых к исполнению давайте сделаем ещё очередь ожидания wiq и туда будем брать и отгружать вот эти вот самые рутины Окей интересная концепция но снова вопрос снова вопрос связанный с чем не будут ли рутины голодать в этой очереди а что я подразумеваю под голоданием но опять-таки как разработчики Гугла мы такие держали в голове определённый Корнер кейс и нарисовали такую ситуацию Что произойдёт если у нас будет происходить примерно такая картина То есть у меня есть определённая рутина которая освободила какой-то МКС на котором вот эти рутины были заблокированы и что происходит у меня есть ещё одна рутина которая вот после разблокировки пришла и хочет взять этот она вызвала и она зах но когда э рутина разблокировала к она о повела вот эти рутины о том что разблокирован вот эта тинка пытается вызвать лок но она не успела потому что здесь строилась другая рутина зря разбудили эту рутину снова она перешла в состояние вейтинг Ну то есть вообще грубо говоря бесполезная операция разбудили рутину которую не получилось взять к а вот эти рутины как раз таки находятся в той очереди на ожидание это этой самой разблокировки делать Вот это был нормальный режим выполнения Давайте сделать отдельный режим starvation Mode starvation Mode когда мы понимаем что допустим определённая у нас рутина висит очень долго голодает например она больше там 1но миллисекунды находится в этой очереди то в данной ситуации мы переключаем starvation Mode и что мы здесь будем делать мы будем делать следующее вот эта рутина будет разблокировать этот КС здесь единственно Циферки немного вот тоже поехали Она разблокирует МКС и затем она берёт и как бы вот эту рутину ставит на этот мьютекс ну то есть как бы перед тем как вообще отдать этот мьютекс она сразу явно выставляет туда рутину то есть не даёт пространство для того чтобы между разблокировкой Текса и блокировкой вот этой грудины кто-то встроили этого когда приходит вот эта грудина то есть здесь уже третий шаг повторюсь юто уже заблокирован потому что А как раз-таки Я перешёл в режим старшина то есть голодания Да чтобы не дать вот этой wiq голодать достаточно продолжительное количество времени вот а дальше снова проблемы проблем очень много да циклы как быть с циклами что я подразумеваю под циклами большинство веб-сервисов это как правило аба нагрузка Ну то есть опять-таки повторюсь Оё Бау нагрузка сходили в базу данных прочитали снова пошли в базу данных там написали пошли в сеть больше мы мало делаем CPU Бау нагрузки мы мало там я не знаю считаем какие-то уравнения что-то там процесс Я не знаю шифру де кодируем но так или иначе есть такие веб-сервисы которые выполняют какой-то компьют то есть есть веб-сервисы которые обсчитывают очень много всего как раз таки на процессоре Вот и более того мало того что есть иба нагрузка есть ещё бесконечные циклы Ну грубо говоря если программист случайно там взял и несколько бесконечных циклов повесил несколько потоков просто греются не выполняют никакой никакой полезной работы мы могли бы подумать что ну Казалось бы наверное это проблема вот этих самых программистов которые взяли посадили вот эти бесконечные циклы на каких-то потоков и теперь вот эти самые рутины никогда не вытеснять с этих потоков потому что там другие потоки придут рано или поздно рутину украдут из из стой очереди и там вообще не будет рутины но Давайте подумаем вот тоже как разработчики Гугла потенциальные что можно было бы сделать А давайте В таких ситуациях То есть у нас Кооперативная многозадачность У нас есть определённые точки в которых вытесняются рутины самостоятельно опять-таки программисты которые будут писать на голову ничего не будут делать для этого чтобы они теснясь но давайте для бесконечных циклов для каких-то определённых CPU Bound задач брать и вытеснять рутины то есть добавим немного щепотку вытесняющее многозадачности Да вопрос как это сделать Ну как это сделать Мы можем в циклах явно компилятором проставлять вот эти точки для переключения контекста будет работать неэффективно потому что ну у нас будет появляться дополнительны latency Зачем Когда вытесняя Когда нужно это дополнительные операции не хотелось бы лишний раз делать Зачем можно отдать это на волю программистов чтобы программисты писали R Go SH они могут забывать Но если они посадили бесконечный цикл на какой-то поток они могут и забыть НТА Go написать и всякое прочее да давайте добавим асинхронную preemption асинхронную вытесняющая многозадачность да то есть в чём идея как только обнаруживается рутина работающая более 10 миллисекунд в поток передаётся сигнал для её вытеснения сигналы стоят копейки Ну то есть грубо говоря У меня есть какой-то поток в данном случае cism а в рамках операционной системы Linux можно от одного потока послать другому потоку того же процесса сигнал в данном случае используется sigur и я передаю сигнал этот сигнал обрабатывается как раз-таки потоком в обработчике И после этого моя рутина будет вытеснять Что значит будет у меня здесь такое используется асинхронная вытесняющее многозадачность она не синхронная чтоб ну то есть я могу не сразу вытеснить Почему Потому что я могу находить в потере в ранта Go есть ещё SA участки кода ий участки кода Вот Но кажется что это уже отдельная тема можно будет про неё прочитать при разборе там сборщика мусора и локатора памяти Go Вот но здесь просто стоит понимать что рутина может вытянуться не сразу потому что она находится в нци в пространстве когда она дойдёт до сейв поинта в тот момент она как раз-таки уже будет вытеснять вот а да и дальше я хотел бы да тут как раз-таки в чатике вопросы Я предлагаю в конце поотрываю теорию Что такое планировщик Какие он задачи выполняет Да Кажется что на этой теории далеко не уедешь Ну то есть она нужна Для чего Ну для какой-то уверенности чтобы ты понимал как твой инструмент работает чтобы мог собеседование пройти но больше ты вряд ли что сделаешь но эта теория тебе открывает как бы врата в то чтобы понять А как вообще рутины работают как их синхронизировать и что можно интересного делать с этим всем и вот что можно интересного со всем этим делать я обычно рассказываю на своём курсе который я где-то полгода на полгода назад сделал это уже будет второй поток этого курса по concurrency в рамках этого курса что будет Ну во-первых скажу что он не для всех ну то есть э если вы не знаете Go я не рекомендую крайне туда идти Потому что я не буду рассказывать Что такое мапы слайсы функции всякие прочие такие штуки они в курсе повсеместно используются У вас должны быть уверенное знания на Go потому что курс исключительно по кон на Go но но язык Go я там не рассказываю вот и что там вообще будет ну то есть глубокие знания по конкуренци будут будут льный код получится написать получится множество паров получится изучить по параллельному программированию получится на реальных задачах которые приходится разрабатывать Ну не то кажды день достаточно часто на практике связано с на собеседованиях получится с этим позаниматься получится в виде домашки и потом итогового проекта получится написать база данных с валом и репликации да получится будет не просто вот но получится и дополнительно ещ можно научиться решать задачки по которые часто встречаются на собеседованиях по вот курс пото [музыка] занимает 11 уроков бегло просто расскажу что там будет первый урок нуно То есть я склоняюсь к такому подходу трудно ехать дальше изучать какие-то рутин изучать какие-то мьюта всякое прочее когда ты не понимаешь как работает компьютер как вообще под капотом выполняется concurrency там что такое concurrency В чём разница параллелизма и так далее какие системные средства для паралельни кода то есть здесь просто рассказывается про процессоры про операционные системы и про модели Кооперативная много вытесняющая многого такое программы в ЧМ разница в общем это база компютер на которую в будущем накладывается Гошка в первом уроке вообще нет Go Хотя курс называется conc Go второй урок уже интересный он более приближен Go он тоже ещё теоретически с какой-то точки зрения потому что здесь важно понять что такое обще рутина как она под капотом устроена Что такое планировщик вот планировщик мы с ми сегодня посмотрели Что значит после этого уже можно переходить практи Когда у нас есть какой-то бэкграунд Когда у нас есть какой-то фундамент под ногами что это за практика Ну то есть это примитивная синхронизация всем нам известная но не просто То есть все мы наверное умеем синхронизировать мьютексы Все мы умеем там ну не то что писать использовать но здесь хотелось бы это рассматривать с той точки зрения А как набивать шишки Как садить дедлок Локи дата рейсы starvation как от них избавляться Что такое инверсия приоритетов как понимать когда у меня произойдёт Deadlock не произойдёт при помощи диаграммой всего такого интересного Здесь много примете организации будут не на одном уроке на втором здесь мы напишем свой read wr mutex посмотрим как вообще это пишется Да разберёмся Что такое Time utex Как можно к написать то есть здесь мы Аля переписываем много чего интересного и вот эти вещи они как бы открывают глаза на то как что-то работает в Go и как что-то можно использовать или переписывать самостоятельно для тех или иных решений тут и посмотрим как устроены эти см ютекс как их в Go можно написать дальше каналы их внутреннего устройства урок простой рассказывается про то что такое каналы Как работать с ними неправильно Как работать правильно как они под капотом устроены где и как их использовать следующий урок паттерны использования каналов банально просто используем каналы на практике смотрим различные паттерны приёмы здесь Их достаточно много вот контексты контексты важна сть которая в последних версиях коро используется контекстов разно смотрим чем они отличаются когда и какое использовать и готовимся к продвинутым лекциям Ну то есть вот до этого момента была такая знаете я не знаю не то что bic часть курса это такая вот разделение следом после этого идёт ADV часть курса где рассматривается очень много достаточно сложных и нетривиальных тем здесь мы делаем подготовку здесь мы изучаем барьеры памяти потому что в будущем они нам потенциально понадобится и вот она как раз-таки часть курса которую я вот долго прораба собирал материалы То есть это лоф структуры данных к тра очередь mcdee алгоритмы синхронизации грубая тонкая оптимистичная не блокирующая оптимизация аба проблемы Хоть она в принципе Go не применима Но это как бы основы которые нужно знать шардирование структур данных rcu всё здесь будет затем урок которого не было в предыдущем потоке но добавился здесь это как раз-таки практическое применение вот этой самой concurrency смотрим на архитектуру веб-сервер синхронные веб-сервера асинхронные веб-сервере смотрим Что такое VP здесь будет немного сижки Я на сижки покажу как это работает чтобы примерно понимать как это внутри Go работало изоляция транзакций в базах данных Смотрим как вообще вот база данных - Это хороший инструмент для практики такого вот конкуренци смотрим на то как база данных справляется с множеством одновременных либо конкурентных грубо говоря запросов неё да как она это вообще всё обрабатывает Какие есть модели здесь просто рассматриваем способы реализации этих самоизоляции транзакций TL подход mvc подход Ну прямо на гошко берём и примитивное в in Memory это всё релизом вот дополнительно смотрим на законом дала и знакомимся с акторно моделью тоже этого занятия не было в предыдущем потоке здесь как раз-таки смотрим на тему того как профилировать concurrency код Как тестировать как отлаживать потому что это не всегда бывает тривиально последнее занятие - это ла кодинг то есть на предыдущем занятии У меня подготовлен везде код его даже можно найти на гитхабе там если вам будет интересно А здесь получается кода Никакого не будет то есть мы просто открываем вместе с вами программируем и пишем код пишем то что чаще всего приходится писать либо на работе либо на собеседованиях вот 11 занятий Как проходит обучение Ну это онлайн уроки в Зуме я приверженец интерактивного подхода к обучению То есть у нас нет курсов записи То есть я всё-таки про то чтобы в Зуме проводить это занятие чтобы люди как раз таки могли в процессе задавать вопросы интересоваться домашка домашка по тому как писать базу данных Вот то есть в рамках этой домашки будете спе написать сво мемори базу данных с валом и асинхронной репликации ну разбор кейсов и жизни почему это важно Ну потому что Казалось бы было бы нелогично сделать такой курс который нельзя было использовать в практике то есть здесь очень много различных паттернов приёмов и тех штук которые встречаются в прикладной разработке чтобы потом это всё использовать в практике дипломный проект опять-таки Memory база данных с репликация синхронным баллом которые если написать можно на собеседованиях презентовать дополнительные материалы Ну потому что есть такие люди которым интересно е что-то глубже посмотреть ещ где-то углубиться и я как правило вот оставляю интересные там ссылки статьи определённую литературы на всего этого ну и сертификат тоже в конце варианты участия Какие всего два то лиш это стандарт сейчас как раз таки есть действует скидка в размере 2000 руб стандарт - это 11 занятий бессрочная запись курса записи с вами остаются навсегда домашка после каждой лекции дипломный проект база данных поддержка в чате Ну то есть общий чатик дополнительные материалы в випе ещё добавляется проверка всех работ мной от преподавателя и Q один раз в неделю Что такое Q то есть мы раз в неделю созваниваемся с VIP участниками и обсуждаем различные вопросы вопросы обычно возникают по домашке по зада по курсу по материалам ещё какие-то рабочие Да вот вопросы это мы всё там обсуждаем вот нуно в рамках первого стандарта у нас на предыдущем потоке ученики часто коллабов друг друга смотрели как раз таки эту самую асинхронную базу данных что что хотят домашки хочу сказать я написал эту inm базу данных с асинхронной репликации референс есть то есть референс есть вы когда будете писать домашки я буду объяснять как это делать и есть референс в виде моего референсного кода то есть на него можно будет смотреть и от него отталкиваться но он вас не обязывает писать именно так же как я он просто Ну я не знаю как дополнительная плюшка которая позволит быстрее написать проект и быстрее разобраться с этим скидка на 2000 действует 24 часа то есть она действительно только для тех кто Вот сегодня был на открытом уроке и осталось 9 дней насколько я помню осталось там 15 или 14 мест то есть количество мест ограничено тут как бы не буду ничего больше говорить кажется что всё и так понятно материалы насчёт этого занятия конечно же Я использовал материалы сторонние для подготовки всего этого я их скину в чат открытого урока наверное вот как закончу Спустя какое-то время пришлю туда все материалы