Быть счастливым не означает, что все идеально, это означает, что вы решили смотреть за пределы несовершенства.
Внутреннее программирование, наиболее активное участие в котором имеет параллелизм. Простое понимание:
Параллелизм — это управление несколькими задачами, которые должны выполняться одновременно, а параллелизм — это одновременное выполнение нескольких задач для многоядерных процессоров. Можно понять, что один управляется, а другой управляется.
Параллелизм обычно относится к вводу-выводу, ввод-вывод — это устройство, независимое от ЦП, а устройства ввода-вывода обычно намного медленнее, чем ЦП, поэтому мы ввели концепцию параллелизма, чтобы ЦП мог инициировать несколько операций ввода-вывода одновременно, не дожидаясь завершения. Устройство ввода-вывода для завершения операции. Выполните другой заказ. Принцип — неблокирующая работа + уведомление о событии.
Меня не особо волнует нижний слой железа, главное писать программы, как просто писать параллельный код. На синтаксическом уровне он хорош для параллелизма и очень подходит для серверов, таких как go, node и некоторых функциональных языков. В последнее время я в основном использую node и kotlin.
Тогда при написании параллельного кода вы будете время от времени задумываться над таким вопросом:
один вопрос
Когда код сталкивается с процессом, который «не может быть временно завершен» (например, установление tcp-ссылки, для установления которой может потребоваться 5 мс), он не хочет здесь блокировать сон, а хочет временно покинуть сцену, чтобы заняться другими делами ( например, посмотрите на другой, можно ли получить установленную ссылку). Вопрос в следующем: после ухода со сцены, когда вы возвращаетесь, контекст все еще выглядит так, как когда вы ушли?
В любом языке есть два основных способа отскочить: 1) вернуться из текущей функции; 2) вызвать новую функцию. Первый уничтожит все локальные переменные и параметры функции в контексте, если только не сохранит эти переменные в отдельном месте перед возвратом; второй может защитить память всего контекста (разве что некоторые регистры будут уничтожены после переключения сопрограммы), И отскок назад тоже обычный способ: возврат функции.
При написании узла это в основном асинхронный/ожидающий без мозгов. Каждый раз, когда я вижу функцию обратного вызова, мое обсессивно-компульсивное расстройство фиксируется, и я всегда пытаюсь преобразовать этот метод в обещание, а затем использую ожидание для получения результата. Brainless попробовал утилиты bluebird и node, хотя некоторые из них очень полезны, но некоторые все еще не могут оправдать моих ожиданий. Полагаясь на безмозглый async/await, многие функции реализуются, и код пишется быстро, но если вы сосредоточены только на ведении бизнеса, не задумываясь глубоко, это плохая производительность, поэтому я остановился и перерыл много async/await, особенно благодаря Учителю Жуань Ифэну я многому научился.
js асинхронное программирование
Поскольку js является однопоточным, асинхронное программирование особенно важно для js.
Существуют в основном следующие типы асинхронных реализаций:
-
Перезвоните
callback, дословный перевод с английского — звонить снова.
Так называемая callback-функция заключается в том, чтобы записать второй сегмент задачи в отдельную функцию и вызывать эту функцию напрямую при повторном выполнении задачи.
Сам обратный вызов в порядке, но он боится многократной вложенности.
-
promise
Promise — это новый способ написания, который изменяет горизонтальную вложенность функций обратного вызова на вертикальную загрузку в виде then.
-
сопрограмма
Сопрограмма — это меньшая единица, чем поток.
Процесс выполнения примерно такой:
На первом этапе начинает выполняться сопрограмма A.
На втором шаге сопрограмма А исполняется наполовину, попадает в приостановку, и право исполнения передается сопрограмме Б.
На третьем этапе (через некоторое время) сопрограмма B возвращает право на выполнение.
Четвертый шаг, сопрограмма A возобновляет выполнение.
Давайте подробнее остановимся на сопрограммах позже.
Очевидно, что после того, как go стал популярным, многие языки программирования полагаются на сопрограммы, потому что сопрограммы очень хорошо преобразовывают асинхронную запись в синхронную, уменьшая умственную нагрузку. js точно не отстает.
Эволюция асинхронного письма js
-
generator
ES6 Добавляет функцию генератора, которая представляет собой реализацию COROUTINES. Самая большая функция состоит в том, что использование ключевого слова доходности используется для передачи выполнения справа от функции.
function* gen(x){ var y = yield x + 2; return y; }Отличие от обычных функций в том, что при вызове функции-генератора она возвращает не результат, а внутренний указатель. Вызов следующего метода указателя переместит внутренний указатель (то есть первый сегмент выполнения асинхронной задачи), а встречающийся оператор yield передаст право выполнения и выполнит другой код. При следующем вызове следующего метода указателя функции выполнение продолжается до следующего оператора yield функции.
Хотя функция Генератор очень лаконично выражает асинхронные операции, управление процессом неудобно (т.е. когда выполнять первую стадию и когда выполнять вторую стадию), поэтому создается впечатление, что функция генератора на самом деле является контейнером для асинхронных операций, которые требуется Механизм, запускающий его автоматическое выполнение.
-
Преобразователь функция
Когда дело доходит до функций преобразования, мы должны сначала понять стратегию оценки параметров.
let m=1; function f(x){ return x*2 } f(m+5)-
вызов по значению
Сначала вычислите значение 6 из m+5, а затем передайте значение функции f, то есть 6*2
-
называть по имени
Передайте m+5 в f и вычислите его при использовании, т.е. (x+5)*2.
Вызов по значению относительно прост, но когда параметр оценивается, параметр фактически не используется, что может привести к потере производительности.
Реализация компилятора "вызов по имени" часто помещает параметры во временную функцию, а затем передает временную функцию в тело функции. Эта временная функция называется функцией Thunk.
js вызывается по значению. Его функция thunk заменяет функцию с несколькими параметрами версией с одним параметром и принимает только функции обратного вызова в качестве параметров.
Таким образом, автоматический исполнитель генератора, основанный на функции thunk, может быть легко реализован.
Конкретную реализацию и способы ее использования см.Уууу. Руан Ифэн.com/blog/2015/0…
-
-
со-функция
Функция co является автоматическим исполнителем функции генератора на основе обещаний.
Исходный код всего несколько десятков строк, а tj god слишком силен👍.
-
async/await
Асинхронные функции — это синтаксический сахар для функций-генераторов.
Асинхронная функция идет со своим исполнителем.При написании асинхронных и ожидающих без мозгов почти все функции пишутся как асинхронные функции.Пока методы, которые нужно ждать, использовать ожидание для ожидания, в результате чего получается много бессмысленных ожидающий. Изначально две несвязанные операции, если каждая из них использует ожидание и т. д., это сильно повлияет на производительность.
Когда несколько запросов выполняются одновременно, попробуйте использовать метод Promise.all.
Поняв описанный выше процесс эволюции, я чувствую, что наконец-то избавился от оружия java-мышления и начал работать с node. Потом, синхронно написав kotlin проект, снова попал в трясину.
Future, RxJava, Actor и сопрограммы kotlin
Я не очень хорошо в этом разбираюсь, пожалуйста, спросите о научно-популярных.
Когда я писал на java, то без мозгов использовал пул потоков и открывал несколько потоков для его обработки, вообще то результат потока в данном случае не нужен.
-
future
Поскольку возвращаемое значение функции нельзя получить напрямую из других потоков, возникает будущее.
FUTRUE может отслеживать ситуацию целевого потока для вызова CALL.При вызове метода FUTURE GET() для получения результата текущий поток начинает блокироваться, а метод прямого вызова завершает возвращаемый результат.
Сам объект Future можно рассматривать как явную ссылку, ссылку на результат асинхронной обработки. Из-за своей асинхронной природы объект, на который он ссылается, может быть недоступен во время создания (например, в работе, передаче по сети или ожидании). В это время, если поток программы, который получает Future, не стремится использовать объект, на который ссылается Future, тогда он может делать все, что захочет.Когда процесс переходит к потребности в объекте, на который ссылается Future, может быть две ситуации:
-
Надеемся увидеть этот объект доступным и завершить некоторые связанные с ним последующие процессы.
Вы можете определить, готов ли указанный объект, вызвав Future.isDone(), и выполнить другую обработку.
-
Если он действительно недоступен, можно ввести и другие отраслевые процессы.
Просто вызовите get() или get(long timeout, TimeUnit unit), чтобы дождаться готовности объекта путем синхронной блокировки. Будет ли фактическая среда выполнения блокироваться или немедленно возвращаться, зависит от времени вызова get() и порядка, в котором объект готов.
-
-
rxjava
-
actor
-
coroutine
Стоять на коленях и выпрашивать популярную науку, а затем улучшать ее после понимания.
Говоря о сопрограммах
Когда дело доходит до сопрограмм, мы должны говорить о потоках.
Поток — это концепция пользовательского режима операционной системы, и сам поток также использует прерывания для планирования. Ранняя параллельная обработка ввода-вывода в пользовательском режиме заключается в использовании модели опроса (выбора) для опроса состояния ввода-вывода, а затем инициации соответствующей операции ввода-вывода, которая называется асинхронной моделью, реагирующей на события.Этот метод не прост в использовании, поэтому он был разработан.Блокирующие операции ввода-вывода позволяют логике приостанавливать и ждать завершения ввода-вывода.Чтобы разрешить блокировку ввода-вывода, чтобы быть параллельным, он должен полагаться на многопоточную или многопроцессную модель для реализации. Однако накладные расходы потоков очень велики, а многопоточная модель не подходит для крупномасштабного параллелизма. Поэтому, когда происходит масштабный параллелизм, мы возвращаемся к использованию ответа на событие. Epoll по-прежнему является моделью опроса, но реализация оптимизирована с точки зрения алгоритмов. В настоящее время мы можем обрабатывать десятки тысяч одновременных запросов с только одна нить.
До появления многоядерных ЦП мы обнаружили, что только один поток не может использовать мощность многоядерных ЦП, поэтому снова был введен пул потоков, чтобы разделить потребление ЦП операций ввода-вывода, и даже ответы на прерывания ЦП можно было использовать совместно. и выполняются несколькими ядрами. Количество потоков в настоящее время примерно равно количеству ядер ЦП и намного меньше, чем количество одновременных операций ввода-вывода (ЦП в настоящее время может обрабатывать миллионы параллельных операций). Введение потоков полностью для балансировки нагрузки и не имеет ничего общего с параллелизмом. Следовательно, независимо от того, используется ли на логическом уровне select/epoll/iocp, асинхронной операции, основанной на ответе на событие, избежать нельзя.Столкнувшись со сложностью самой асинхронной логики, мы ввели async/await и сопрограмму для уменьшения сложности.
Корутина — это очень широкое понятие, и async/await — тоже тип сопрограммы.
Режим реализации сопрограммы делится на сопрограмму со стеком и сопрограмму без стека.
Так называемый stackful означает, что каждая сопрограмма имеет независимый рабочий стек.Например, каждая горутина в языке go будет выделять 4 КБ памяти в качестве рабочего стека, и рабочий стек также будет переключаться при переключении горутины. Преимущество stackful в том, что эта сопрограмма завершена, и сопрограммы могут быть вложены и зациклены.
Stackful соответствует сопрограмма без стека, такая как функция генератора js.Этот тип сопрограммы не требует выделения отдельного пространства стека, а состояние сопрограммы хранится в замыкании, но недостатком является то, что функция относительно слаба, не может можно назвать вложенными, и нет возможности взаимодействовать с асинхронными функциями, которые используются вместе для планирования потока управления, поэтому в принципе нет возможности сравнить со стековыми сопрограммами. При сохранении этих состояний некоторые языки вводят модель конечного автомата для реализации потоков.
Появление async/await реализует полную сопрограмму, основанную на бесстековой сопрограмме. По характеристикам она очень близка к stackful coroutine, которая может не только быть вложенной, но и поддерживать try catch.
Эпилог
Я обнаружил, что у меня совсем нет мозгов: как только я чему-то научился, я использую это бездумно и не буду думать, пока не упрусь в стену.